Summary
getByRole('option', { name: … }) cannot match any NcSelect option whose label is 10 characters or longer, in any app in the fleet. The option's accessible name has a space injected into the middle of it.
Measured on openbuild (NC 34, @nextcloud/vue 9.9.0) while fixing tests/e2e/automations-rbac.spec.ts. The application is called "RBAC Automations App". Playwright's page snapshot:
- option "RBAC Autom ations App" # <- space inside "Automations"
- generic "RBAC Automations App" # correct, but only via title=
- generic: RBAC Autom
- generic: ations App
Cause
NcEllipsisedOption implements middle-ellipsis by splitting the label into two <span>s:
needsTruncate() { return this.name && this.name.length >= 10 }
split() { return this.name.length - Math.min(Math.floor(this.name.length / 2), 10) }
part1 = name.slice(0, split)
part2 = name.slice(split)
The wrapper <span class="name-parts" title="{full name}"> carries the correct name, so the inner node's accessible name is right. But the option role computes its name from contents, and the accessible-name algorithm inserts a space at each element boundary — so the option announces the mangled form.
Every label of 10+ characters is affected, at index len - min(floor(len/2), 10):
| label |
len |
announced as |
RBAC Automations App |
20 |
RBAC Autom ations App |
PW Version Lifecycle |
20 |
PW Version Lifecycle (splits at a real space, so invisible) |
production |
10 |
produ ction |
development |
11 |
develo pment |
Note the third row: short, ordinary values like production are affected too, which is easy to miss.
Why this is worth a fleet-level issue
Accessibility. A screen reader reads the mangled name. WCAG 2.2 AA 4.1.2 (Name, Role, Value); arguably 2.5.3 (Label in Name) too, since the accessible name no longer contains the visible label as a contiguous string.
It has already produced two wrong issues. In openbuild the locator timed out, the timeout was read as "the fixture is missing / the editor cannot see the application", and that inference became ConductionNL/openbuild#171 and the second comment on ConductionNL/openbuild#173 — the latter an architecture issue. Both are now retracted; the API was measured directly and works. A locator that finds nothing says nothing about the API underneath it, and this is a locator that finds nothing for a reason that has no connection to the app at all.
Six other agents are writing Playwright role queries against NcSelect right now.
The honest workaround
Target the component's own record of the full name:
function selectOption(page: Page, label: string) {
return page.getByRole('option').filter({ has: page.locator(`[title="${label}"]`) })
}
Do not relax the regex to tolerate arbitrary whitespace: that hides a real a11y defect and will match the wrong option in a longer list.
Where the fix belongs
NcEllipsisedOption is upstream @nextcloud/vue, not @conduction/nextcloud-vue, so we cannot fix it by cutting one of our own releases. The upstream fix is small — give the split wrapper an explicit aria-label (or role="text"/aria-hidden on the parts) so the accessible name is computed from the whole name rather than from the two fragments. This issue tracks (a) the fleet-wide test-authoring guidance above and (b) raising it upstream.
Confirmed by: Playwright page snapshot on a live instance, plus reading node_modules/@nextcloud/vue/dist/chunks/NcEllipsisedOption-*.mjs.
Summary
getByRole('option', { name: … })cannot match anyNcSelectoption whose label is 10 characters or longer, in any app in the fleet. The option's accessible name has a space injected into the middle of it.Measured on openbuild (NC 34,
@nextcloud/vue9.9.0) while fixingtests/e2e/automations-rbac.spec.ts. The application is called "RBAC Automations App". Playwright's page snapshot:Cause
NcEllipsisedOptionimplements middle-ellipsis by splitting the label into two<span>s:The wrapper
<span class="name-parts" title="{full name}">carries the correct name, so the inner node's accessible name is right. But theoptionrole computes its name from contents, and the accessible-name algorithm inserts a space at each element boundary — so the option announces the mangled form.Every label of 10+ characters is affected, at index
len - min(floor(len/2), 10):RBAC Automations AppRBAC Autom ations AppPW Version LifecyclePW Version Lifecycle(splits at a real space, so invisible)productionprodu ctiondevelopmentdevelo pmentNote the third row: short, ordinary values like
productionare affected too, which is easy to miss.Why this is worth a fleet-level issue
Accessibility. A screen reader reads the mangled name. WCAG 2.2 AA 4.1.2 (Name, Role, Value); arguably 2.5.3 (Label in Name) too, since the accessible name no longer contains the visible label as a contiguous string.
It has already produced two wrong issues. In openbuild the locator timed out, the timeout was read as "the fixture is missing / the editor cannot see the application", and that inference became ConductionNL/openbuild#171 and the second comment on ConductionNL/openbuild#173 — the latter an architecture issue. Both are now retracted; the API was measured directly and works. A locator that finds nothing says nothing about the API underneath it, and this is a locator that finds nothing for a reason that has no connection to the app at all.
Six other agents are writing Playwright role queries against
NcSelectright now.The honest workaround
Target the component's own record of the full name:
Do not relax the regex to tolerate arbitrary whitespace: that hides a real a11y defect and will match the wrong option in a longer list.
Where the fix belongs
NcEllipsisedOptionis upstream@nextcloud/vue, not@conduction/nextcloud-vue, so we cannot fix it by cutting one of our own releases. The upstream fix is small — give the split wrapper an explicitaria-label(orrole="text"/aria-hiddenon the parts) so the accessible name is computed from the whole name rather than from the two fragments. This issue tracks (a) the fleet-wide test-authoring guidance above and (b) raising it upstream.Confirmed by: Playwright page snapshot on a live instance, plus reading
node_modules/@nextcloud/vue/dist/chunks/NcEllipsisedOption-*.mjs.