The accessibility walk claims 35 pages it never opens, and two it opens empty run off a phone once they have something on them #167

Closed
opened 2026-09-10 11:16:07 +00:00 by tiagoagueda · 2 comments
Owner

Observation

Found while fixing #165. Two things, the second larger than the first.

1. Pages the walk opens empty, and what they do with something on them

The browser suite's walk (signed_in_paths in tests/e2e/test_accessibility.py, which axe-core and the reflow check both use) visits /applications/suggestions/, /jobs/captures/ and /documents/cvs/. Its fixture creates no suggestion, no capture and no CV with an entry, so those pages are only ever checked in their empty state.

I gave the walk that content in a scratch test (a pending suggestion not yet matched to an application, a pending capture, a CV with one entry) and ran the reflow check at 320 pixels. Two pages run off the side of a phone:

Page English Greek German
/applications/suggestions/, one pending suggestion 45 px (78) 60 px (92) 70 px (106)
/documents/cvs/<pk>/ 0 26 px (46) 19 px (52)

The first figure uses this machine's fonts. The one in brackets uses a font as wide as the Linux runner's, which is closer to what CI and an Android phone draw (see #165). The capture list, the listings page with a capture waiting, and everything else were clean in all three languages.

  • Suggestions. The accept form (a Which application? select, and the buttons beside it) sits in <div class="shrink-0">, so it cannot be narrower than all of its contents on one line. It happens in English, for anyone with a suggestion not yet matched to an application, which is the ordinary case for a mail or calendar plugin's first suggestion.
  • A CV's page. Each entry is the row #165 fixed on the career page: its words sit beside a shrink-0 group of ↑, ↓, Tailor and Remove, in a row that does not wrap. In Greek and German that group alone is nearly as wide as the column. The page is a grid whose one column is sized by its content, so the column grows past the screen and takes the Add entries card beneath it along.

2. Pages the suite says it checks and never opens

tests/test_page_coverage.py insists that every URL pattern is "either visited by the browser suite or named in EXCUSED with a reason". But "visited" means "named in VISITED_URL_NAMES", a hand-written tuple beside the walk, and nothing checks the tuple against the walk.

I resolved every path the walk visits, plus every literal path in any browser test, and compared the result with the tuple. 104 names are claimed and 69 are reached. These 35 are claimed and reached by no path:

accounts:recovery_open        documents:letter_delete       jobs:industry_update
applications:interview_outcome documents:letter_detail      jobs:posting_delete
applications:interview_update documents:letter_preview      jobs:posting_detail
applications:tag_delete       documents:letter_update       jobs:posting_update
applications:tag_update       documents:send                listings:apply
connections:create            documents:upload_delete       resume:item_delete
connections:delete            documents:upload_update       server:person_delete
connections:edit              jobs:capture_review           server:person_username
documents:cv_add_items        jobs:company_delete
documents:cv_delete           jobs:contact_delete
documents:cv_detail           jobs:contact_update
documents:cv_item_delete      jobs:industry_delete
documents:cv_item_update
documents:cv_preview
documents:cv_update

Two caveats. The audit reads paths, so a page the smoke test reaches by clicking (documents:send, jobs:capture_review) is counted as not reached. That is fair for this purpose, because axe and the reflow check run only on the walk. And accounts:recovery_open may be a false alarm from how I faked the recovery link. Every CV page and every letter page past the list is on it, and so is the whole of the first finding: nothing measured a CV's page, because nothing opened one.

What fixing it is

  • Derive the claim from the walk. Build VISITED_URL_NAMES by resolving the paths the walk actually visits, rather than writing it by hand, so a claim cannot get ahead of the walk. Then the coverage test's failures are the honest list of what still needs a path.
  • Give the walk the content those paths need. Add a pending suggestion without an application, a pending capture, a CV with an entry, a letter, an interview, a tag, a contact and an upload to furnished, with their pages in signed_in_paths. The object pages are what axe and the reflow check have never seen.
  • Fix the two pages the way #165 fixed the others. Let the group wrap instead of shrink-0, and let the words claim twelve rem (grow basis-48) before anything sits beside them.

Worth being careful about

  • axe will see new content too. A fuller walk may find accessibility violations, not only layout ones, on pages nobody has checked. Fix them here rather than exclude them.
  • Delete pages are confirmation forms, safe to open and not to submit. The walk only fetches, as it already does for the recovery link.
  • The walk gets longer. It runs three times for reflow (#165) and twice for axe (light and dark). Thirty more pages is a real cost, and worth it for pages that have never been looked at.

Classification

Bug, accessibility. One page fails WCAG 2.2 SC 1.4.10 in English, and the suite's claim of coverage is a third larger than its coverage.

## Observation Found while fixing #165. Two things, the second larger than the first. ### 1. Pages the walk opens empty, and what they do with something on them The browser suite's walk (`signed_in_paths` in `tests/e2e/test_accessibility.py`, which axe-core and the reflow check both use) visits `/applications/suggestions/`, `/jobs/captures/` and `/documents/cvs/`. Its fixture creates no suggestion, no capture and no CV with an entry, so those pages are only ever checked in their empty state. I gave the walk that content in a scratch test (a pending suggestion not yet matched to an application, a pending capture, a CV with one entry) and ran the reflow check at 320 pixels. Two pages run off the side of a phone: | Page | English | Greek | German | | --- | --- | --- | --- | | `/applications/suggestions/`, one pending suggestion | **45 px** (78) | 60 px (92) | 70 px (106) | | `/documents/cvs/<pk>/` | 0 | 26 px (46) | 19 px (52) | The first figure uses this machine's fonts. The one in brackets uses a font as wide as the Linux runner's, which is closer to what CI and an Android phone draw (see #165). The capture list, the listings page with a capture waiting, and everything else were clean in all three languages. - **Suggestions.** The accept form (a *Which application?* select, and the buttons beside it) sits in `<div class="shrink-0">`, so it cannot be narrower than all of its contents on one line. It happens **in English**, for anyone with a suggestion not yet matched to an application, which is the ordinary case for a mail or calendar plugin's first suggestion. - **A CV's page.** Each entry is the row #165 fixed on the career page: its words sit beside a `shrink-0` group of ↑, ↓, *Tailor* and *Remove*, in a row that does not wrap. In Greek and German that group alone is nearly as wide as the column. The page is a grid whose one column is sized by its content, so the column grows past the screen and takes the *Add entries* card beneath it along. ### 2. Pages the suite says it checks and never opens `tests/test_page_coverage.py` insists that every URL pattern is *"either visited by the browser suite or named in `EXCUSED` with a reason"*. But "visited" means "named in `VISITED_URL_NAMES`", a hand-written tuple beside the walk, and nothing checks the tuple against the walk. I resolved every path the walk visits, plus every literal path in any browser test, and compared the result with the tuple. **104 names are claimed and 69 are reached.** These 35 are claimed and reached by no path: ``` accounts:recovery_open documents:letter_delete jobs:industry_update applications:interview_outcome documents:letter_detail jobs:posting_delete applications:interview_update documents:letter_preview jobs:posting_detail applications:tag_delete documents:letter_update jobs:posting_update applications:tag_update documents:send listings:apply connections:create documents:upload_delete resume:item_delete connections:delete documents:upload_update server:person_delete connections:edit jobs:capture_review server:person_username documents:cv_add_items jobs:company_delete documents:cv_delete jobs:contact_delete documents:cv_detail jobs:contact_update documents:cv_item_delete jobs:industry_delete documents:cv_item_update documents:cv_preview documents:cv_update ``` Two caveats. The audit reads paths, so a page the smoke test reaches by clicking (`documents:send`, `jobs:capture_review`) is counted as not reached. That is fair for this purpose, because axe and the reflow check run only on the walk. And `accounts:recovery_open` may be a false alarm from how I faked the recovery link. Every CV page and every letter page past the list is on it, and so is the whole of the first finding: nothing measured a CV's page, because nothing opened one. ## What fixing it is - **Derive the claim from the walk.** Build `VISITED_URL_NAMES` by resolving the paths the walk actually visits, rather than writing it by hand, so a claim cannot get ahead of the walk. Then the coverage test's failures are the honest list of what still needs a path. - **Give the walk the content those paths need.** Add a pending suggestion without an application, a pending capture, a CV with an entry, a letter, an interview, a tag, a contact and an upload to `furnished`, with their pages in `signed_in_paths`. The object pages are what axe and the reflow check have never seen. - **Fix the two pages the way #165 fixed the others.** Let the group wrap instead of `shrink-0`, and let the words claim twelve rem (`grow basis-48`) before anything sits beside them. ## Worth being careful about - **axe will see new content too.** A fuller walk may find accessibility violations, not only layout ones, on pages nobody has checked. Fix them here rather than exclude them. - **Delete pages are confirmation forms**, safe to open and not to submit. The walk only fetches, as it already does for the recovery link. - **The walk gets longer.** It runs three times for reflow (#165) and twice for axe (light and dark). Thirty more pages is a real cost, and worth it for pages that have never been looked at. ## Classification Bug, accessibility. One page fails WCAG 2.2 SC 1.4.10 in English, and the suite's claim of coverage is a third larger than its coverage.
tiagoagueda added this to the 0.3.0 milestone 2026-09-10 11:16:07 +00:00
Author
Owner

Landed on 0.3.0 as d4b8bf3d6.

The claim is derived from the walk now, so it cannot get ahead of it again — and the honest
list it produced was 34, not 35: accounts:recovery_open was the false alarm this issue
suspected it might be.

The fuller walk found six faults, not two. Besides the CV page this issue measured, it
found the uploads list (a title in 58 pixels in English, 13 in Greek), the connections list
(flex-1 is flex: 1 1 0%, so the words claimed no width before anything wrapped), a
20-pixel checkbox on the applications filter against SC 2.5.8, and an aria-describedby on
the capture review page pointing at an element that was not there — worse than no
description, because a screen reader is told there is one.

The upload edit form is the one worth repeating. It scrolled sideways by exactly the
same amount in every language
, because Django renders a bound file field as
Currently: documents/1/2026/09/reference.txt — a path, no spaces, 252 unbreakable pixels
against the 238 a phone leaves. The new form passed all along, which is precisely why
nothing had caught it, and it is the clearest example of what this issue is about: the walk
opened the empty form and never a filled one.

One exemption, against this issue's instruction, deliberately

#167 says violations on newly-walked pages get fixed rather than excluded, and the other
thirty were. The preview pages are different in kind. CVPreviewView returns "the CV as
HTML, exactly as the PDF renderer will see it"
, so axe is reading a print document and
reporting that it has no <main>. Satisfying that means putting a landmark into a CV theme,
which changes every PDF Postulo produces in order to answer a question nobody asks of a
printed page. They stay in the walk — reflow and target size still read them, and a CV
preview running off a phone would still fail — and only axe looks away, with the reason
written where the exemption is.

Not done here

The suggestions form. This issue measured it at 45px over in English, and the walk still
has no pending suggestion, so nothing reached it. That wants one in furnished and is worth
its own look rather than being bundled in.

Turned up on the way

That bound file field shows the person their internal storage path — account id and
upload month included — where the filename would do. Letting it wrap fixes the overflow and
does nothing about that. Filed separately.

Landed on `0.3.0` as `d4b8bf3d6`. The claim is derived from the walk now, so it cannot get ahead of it again — and the honest list it produced was **34**, not 35: `accounts:recovery_open` was the false alarm this issue suspected it might be. **The fuller walk found six faults, not two.** Besides the CV page this issue measured, it found the uploads list (a title in 58 pixels in English, 13 in Greek), the connections list (`flex-1` is `flex: 1 1 0%`, so the words claimed no width before anything wrapped), a 20-pixel checkbox on the applications filter against SC 2.5.8, and an `aria-describedby` on the capture review page pointing at an element that was not there — worse than no description, because a screen reader is told there is one. The upload **edit** form is the one worth repeating. It scrolled sideways by *exactly the same amount in every language*, because Django renders a bound file field as `Currently: documents/1/2026/09/reference.txt` — a path, no spaces, 252 unbreakable pixels against the 238 a phone leaves. The **new** form passed all along, which is precisely why nothing had caught it, and it is the clearest example of what this issue is about: the walk opened the empty form and never a filled one. ### One exemption, against this issue's instruction, deliberately #167 says violations on newly-walked pages get fixed rather than excluded, and the other thirty were. The preview pages are different in kind. `CVPreviewView` returns *"the CV as HTML, exactly as the PDF renderer will see it"*, so axe is reading a **print document** and reporting that it has no `<main>`. Satisfying that means putting a landmark into a CV theme, which changes every PDF Postulo produces in order to answer a question nobody asks of a printed page. They stay in the walk — reflow and target size still read them, and a CV preview running off a phone would still fail — and only axe looks away, with the reason written where the exemption is. ### Not done here **The suggestions form.** This issue measured it at 45px over in English, and the walk still has no pending suggestion, so nothing reached it. That wants one in `furnished` and is worth its own look rather than being bundled in. ### Turned up on the way That bound file field shows the person their **internal storage path** — account id and upload month included — where the filename would do. Letting it wrap fixes the overflow and does nothing about that. Filed separately.
Author
Owner

Landed on main as de7374524, which closes it: the suggestions form was the one thing
d4b8bf3d6 recorded as not done.

Measured first, with a pending, unmatched suggestion on the page at 320 px: 45 px over
in English, 60 in Greek, 70 in German -- this issue's own figures, reproduced -- and every
time the culprit was <div class="shrink-0"> holding a <select> as wide as its widest
option (330-356 px against the 238 a phone leaves).

The same shape #165 gave the other rows: the words claim twelve rem (grow basis-48
rather than flex-1, which claims none), the group beside them may wrap under and shrink,
and the select may give way inside it. Zero over in all three languages afterwards; axe
was clean before and after.

The walk has the content now. furnished files a pending, unmatched suggestion through
suggestions.suggest(), as a mailbox plugin would -- body, context, two proposed dates, a
suggested status -- so the reflow check and axe read /applications/suggestions/ with
something on it from here on, rather than only ever empty.

Landed on `main` as `de7374524`, which closes it: the suggestions form was the one thing `d4b8bf3d6` recorded as not done. **Measured first, with a pending, unmatched suggestion on the page at 320 px:** 45 px over in English, 60 in Greek, 70 in German -- this issue's own figures, reproduced -- and every time the culprit was `<div class="shrink-0">` holding a `<select>` as wide as its widest option (330-356 px against the 238 a phone leaves). **The same shape #165 gave the other rows:** the words claim twelve rem (`grow basis-48` rather than `flex-1`, which claims none), the group beside them may wrap under and shrink, and the select may give way inside it. **Zero over in all three languages** afterwards; axe was clean before and after. **The walk has the content now.** `furnished` files a pending, unmatched suggestion through `suggestions.suggest()`, as a mailbox plugin would -- body, context, two proposed dates, a suggested status -- so the reflow check and axe read `/applications/suggestions/` with something on it from here on, rather than only ever empty.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#167
No description provided.