Update coverage badges [skip ci]
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
"""
|
||||
Regression tests for form field label geometry (spec S15).
|
||||
|
||||
Text renders with a baseline anchor, so drawing the label at the field's origin
|
||||
put its glyphs above that origin - outside the box the field claims through size
|
||||
and in_object. Stacked fields therefore had each label overprinting the input box
|
||||
of the field before it.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
from pyWebLayout.abstract.functional import Form, FormField, FormFieldType
|
||||
from pyWebLayout.concrete.functional import FormFieldText
|
||||
from pyWebLayout.concrete.page import Page
|
||||
from pyWebLayout.layout.document_layouter import form_layouter
|
||||
from pyWebLayout.style import Font
|
||||
from pyWebLayout.style.page_style import PageStyle
|
||||
|
||||
|
||||
ORIGIN = (10, 40)
|
||||
FIELD_HEIGHT = 24
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def font():
|
||||
return Font(font_size=12, colour=(0, 0, 0))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def canvas():
|
||||
image = Image.new("RGB", (300, 200), (255, 255, 255))
|
||||
return image, ImageDraw.Draw(image)
|
||||
|
||||
|
||||
def make_field(font, draw, label="Email Address"):
|
||||
field = FormField(name="email", field_type=FormFieldType.TEXT, label=label)
|
||||
renderable = FormFieldText(field, font, draw, field_height=FIELD_HEIGHT)
|
||||
renderable.set_origin(np.array(list(ORIGIN)))
|
||||
return renderable
|
||||
|
||||
|
||||
def ink_rows(image, x_range, y_range):
|
||||
pixels = image.convert("RGB").load()
|
||||
return [y for y in y_range
|
||||
if any(sum(pixels[x, y]) < 400 for x in x_range)]
|
||||
|
||||
|
||||
class TestLabelStaysInsideTheFieldBox:
|
||||
|
||||
def test_label_ink_is_below_the_origin(self, font, canvas):
|
||||
image, draw = canvas
|
||||
renderable = make_field(font, draw)
|
||||
renderable.render()
|
||||
|
||||
rows = ink_rows(image, range(ORIGIN[0], ORIGIN[0] + 140),
|
||||
range(0, ORIGIN[1]))
|
||||
assert not rows, \
|
||||
f"label drew above its own origin, at rows {rows}"
|
||||
|
||||
def test_label_and_box_do_not_overlap(self, font, canvas):
|
||||
image, draw = canvas
|
||||
renderable = make_field(font, draw)
|
||||
renderable.render()
|
||||
|
||||
ascent, descent = font.font.getmetrics()
|
||||
label_bottom = ORIGIN[1] + ascent + descent
|
||||
box_top = renderable.field_area_offset + ORIGIN[1]
|
||||
|
||||
assert box_top >= label_bottom, \
|
||||
"the input box must start below the label's descenders"
|
||||
|
||||
def test_reported_height_covers_everything_drawn(self, font, canvas):
|
||||
image, draw = canvas
|
||||
renderable = make_field(font, draw)
|
||||
renderable.render()
|
||||
|
||||
top, bottom = ORIGIN[1], ORIGIN[1] + int(renderable.size[1])
|
||||
rows = ink_rows(image, range(ORIGIN[0], ORIGIN[0] + 200), range(0, 200))
|
||||
assert min(rows) >= top, "ink above the field's declared box"
|
||||
assert max(rows) < bottom, "ink below the field's declared box"
|
||||
|
||||
|
||||
class TestStackedFieldsDoNotCollide:
|
||||
|
||||
def test_form_layout_leaves_labels_clear(self, font):
|
||||
page = Page(size=(300, 400), style=PageStyle())
|
||||
form = Form("signup")
|
||||
for name in ["Username", "Email Address", "Password"]:
|
||||
form.add_field(FormField(name=name.lower().replace(" ", "_"),
|
||||
field_type=FormFieldType.TEXT, label=name))
|
||||
|
||||
ok, ids = form_layouter(form, page, font)
|
||||
assert ok and len(ids) == 3
|
||||
|
||||
fields = [c for c in page.children if isinstance(c, FormFieldText)]
|
||||
assert len(fields) == 3
|
||||
|
||||
for earlier, later in zip(fields, fields[1:]):
|
||||
earlier_bottom = earlier.origin[1] + earlier.size[1]
|
||||
assert later.origin[1] >= earlier_bottom, \
|
||||
"fields overlap: a label would print over the preceding input box"
|
||||
|
||||
def test_rendered_form_has_no_ink_collisions(self, font):
|
||||
"""Every field's ink stays within its own declared bounds."""
|
||||
page = Page(size=(300, 400), style=PageStyle())
|
||||
form = Form("signup")
|
||||
for name in ["Username", "Email Address"]:
|
||||
form.add_field(FormField(name=name.lower(), field_type=FormFieldType.TEXT,
|
||||
label=name))
|
||||
form_layouter(form, page, font)
|
||||
image = page.render()
|
||||
|
||||
fields = [c for c in page.children if isinstance(c, FormFieldText)]
|
||||
for field in fields:
|
||||
top = int(field.origin[1])
|
||||
bottom = top + int(field.size[1])
|
||||
rows = ink_rows(image, range(int(field.origin[0]),
|
||||
int(field.origin[0] + field.size[0])),
|
||||
range(max(0, top - 6), top))
|
||||
assert not rows, f"ink found just above a field at y={top}"
|
||||
|
||||
|
||||
class TestClickTargetsFollowTheLayout:
|
||||
|
||||
def test_click_in_the_input_area_focuses(self, font, canvas):
|
||||
_, draw = canvas
|
||||
renderable = make_field(font, draw)
|
||||
|
||||
inside = (5, renderable.field_area_offset + FIELD_HEIGHT // 2)
|
||||
assert renderable.handle_click(inside) is True
|
||||
assert renderable._focused is True
|
||||
|
||||
def test_click_on_the_label_does_not_focus(self, font, canvas):
|
||||
_, draw = canvas
|
||||
renderable = make_field(font, draw)
|
||||
|
||||
on_label = (5, 2)
|
||||
assert renderable.handle_click(on_label) is False
|
||||
Reference in New Issue
Block a user