Skip to content

Commit 252e568

Browse files
committed
test: [OCISDEV-417] add a11y playwright tests
Setup new Playwright a11y tests and add a11y reporter. Accessibility checks are added into existing E2E tests. Docs are added to explain the new accessibility tests.
1 parent a301a68 commit 252e568

File tree

25 files changed

+444
-20
lines changed

25 files changed

+444
-20
lines changed

.drone.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# The version of OCIS to use in pipelines that test against OCIS
2-
OCIS_COMMITID=62f4548d67a6d2e3f693d6ccab72d871ffb0965e
2+
OCIS_COMMITID=84957355589d189c9b752c36b0796e1b0aad07c7
33
OCIS_BRANCH=master

.drone.star

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ def e2eTestsOnPlaywright(ctx):
571571
environment = {
572572
"BASE_URL_OCIS": "ocis:9200",
573573
"PLAYWRIGHT_BROWSERS_PATH": ".playwright",
574+
"TESTS_RUNNER": "playwright",
574575
}
575576

576577
steps += restoreBuildArtifactCache(ctx, "pnpm", ".pnpm-store") + \
@@ -594,6 +595,9 @@ def e2eTestsOnPlaywright(ctx):
594595
],
595596
}]
596597

598+
if not "skip-a11y" in ctx.build.title.lower():
599+
steps += uploadA11yResult(ctx)
600+
597601
pipelines.append({
598602
"kind": "pipeline",
599603
"type": "docker",
@@ -637,6 +641,7 @@ def e2eTests(ctx):
637641
"federationServer": False,
638642
"failOnUncaughtConsoleError": "false",
639643
"extraServerEnvironment": {},
644+
"skipA11y": "false",
640645
}
641646

642647
e2e_trigger = {
@@ -671,6 +676,9 @@ def e2eTests(ctx):
671676
if ("with-tracing" in ctx.build.title.lower()):
672677
params["reportTracing"] = "true"
673678

679+
if "skip-a11y" in ctx.build.title.lower():
680+
params["skipA11y"] = "true"
681+
674682
environment = {
675683
"HEADLESS": "true",
676684
"RETRY": "1",
@@ -680,6 +688,7 @@ def e2eTests(ctx):
680688
"PLAYWRIGHT_BROWSERS_PATH": ".playwright",
681689
"BROWSER": "chromium",
682690
"FEDERATED_BASE_URL_OCIS": "federation-ocis:9200",
691+
"SKIP_A11Y_TESTS": params["skipA11y"],
683692
}
684693

685694
steps += restoreBuildArtifactCache(ctx, "pnpm", ".pnpm-store") + \
@@ -2009,3 +2018,31 @@ def restoreBrowsersCache():
20092018
],
20102019
},
20112020
]
2021+
2022+
def uploadA11yResult(ctx):
2023+
return [
2024+
{
2025+
"name": "upload-a11y-result",
2026+
"image": PLUGINS_S3_IMAGE,
2027+
"pull": "if-not-exists",
2028+
"settings": {
2029+
"bucket": S3_PUBLIC_CACHE_BUCKET,
2030+
"endpoint": S3_CACHE_SERVER,
2031+
"path_style": True,
2032+
"source": "%s/reports/e2e/a11y-report.json" % dir["web"],
2033+
"strip_prefix": "%s/reports/e2e/" % dir["web"],
2034+
"target": "/${DRONE_REPO}/${DRONE_BUILD_NUMBER}/a11y",
2035+
},
2036+
"environment": {
2037+
"AWS_ACCESS_KEY_ID": {
2038+
"from_secret": "cache_public_s3_access_key",
2039+
},
2040+
"AWS_SECRET_ACCESS_KEY": {
2041+
"from_secret": "cache_public_s3_secret_key",
2042+
},
2043+
},
2044+
"when": {
2045+
"status": ["failure", "success"],
2046+
},
2047+
},
2048+
]

docs/testing/accesibility.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
title: 'Accessibility'
3+
date: 2025-11-10T00:00:00+00:00
4+
weight: 60
5+
geekdocRepo: https://github.com/owncloud/web
6+
geekdocEditPath: edit/master/docs/testing
7+
geekdocFilePath: accessibility.md
8+
---
9+
10+
{{< toc >}}
11+
12+
## Introduction
13+
14+
Accessibility is a crucial aspect of web development. It ensures that web applications are usable by everyone, including people with disabilities.
15+
16+
## What Tools We Use
17+
18+
### eslint-plugin-vuejs-accessibility
19+
20+
We use [eslint-plugin-vuejs-accessibility](https://github.com/vue-a11y/eslint-plugin-vuejs-accessibility) to quickly catch accessibility issues in the codebase. This plugin is used in the [@ownclouders/eslint-config](https://www.npmjs.com/package/@ownclouders/eslint-config).
21+
22+
### @axe-core/playwright
23+
24+
We use [@axe-core/playwright](https://github.com/dequelabs/axe-core-npm) to automatically test the accessibility of the ownCloud Web client. All tests are run automatically on every PR and on commits to the `master` branch. We are not running dedicated accessibility tests and instead make them part of the E2E tests. This way we do not have to maintain duplicate tests and we can granularly add accessibility tests to the specific steps of the tests. The tests are considered failed if any `serious` or `critical` accessibility violations are found.
25+
26+
#### Running Accessibility Tests
27+
28+
To run the accessibility tests, you can simply run our existing E2E tests using the following command:
29+
30+
```bash
31+
pnpm test:e2e:cucumber
32+
```
33+
34+
#### Skipping Accessibility Tests Locally
35+
36+
If you want to skip the accessibility tests, you can add the `SKIP_A11Y_TESTS` environment variable to your command.
37+
38+
```bash
39+
SKIP_A11Y_TESTS=true pnpm test:e2e:cucumber
40+
```
41+
42+
#### Skipping Accessibility Tests in CI
43+
44+
If you want to skip the accessibility tests in CI, you can add the `[skip-a11y]` flag into the title of the PR.
45+
46+
#### Accessibility Report
47+
48+
After the tests are run, a JSON accessibility report is generated in the `reports/e2e/a11y-report.json` file. This report contains detailed information about the accessibility violations found in the tests.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"@vue/compiler-dom": "3.5.22",
5858
"@vue/compiler-sfc": "3.5.22",
5959
"@vue/test-utils": "2.4.6",
60+
"axe-core": "^4.11.0",
6061
"browserslist-to-esbuild": "^2.1.1",
6162
"browserslist-useragent-regexp": "^4.1.3",
6263
"commander": "14.0.2",

packages/design-system/src/components/OcLoader/OcLoader.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import OcLoader from './OcLoader.vue'
22
import { mount } from '@ownclouders/web-test-helpers'
33

4+
const selectors = {
5+
label: '[data-testid="oc-loader-label"]'
6+
}
7+
48
describe('OcLoader', () => {
59
function getWrapper(props = {}) {
610
return mount(OcLoader, {
@@ -9,7 +13,7 @@ describe('OcLoader', () => {
913
}
1014
it('should set provided aria-label', () => {
1115
const wrapper = getWrapper({ ariaLabel: 'test' })
12-
expect(wrapper.attributes('aria-label')).toBe('test')
16+
expect(wrapper.find(selectors.label).text()).toBe('test')
1317
})
1418
describe('when prop flat is enabled', () => {
1519
it('should set loader flat class to the wrapper', () => {

packages/design-system/src/components/OcLoader/OcLoader.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<template>
2-
<div :class="['oc-loader', { 'oc-loader-flat': flat }]" :aria-label="ariaLabel" />
2+
<div :class="['oc-loader', { 'oc-loader-flat': flat }]">
3+
<span class="oc-invisible-sr" data-testid="oc-loader-label" v-text="ariaLabel" />
4+
</div>
35
</template>
46

57
<script lang="ts" setup>

packages/web-app-files/src/components/SideBar/Shares/ExpirationDateIndicator.vue

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
<template>
22
<div class="oc-flex oc-flex-center expiration-date-indicator">
3-
<oc-icon
4-
v-oc-tooltip="expirationDateTooltip"
5-
:aria-label="expirationDateTooltip"
6-
name="calendar-event"
7-
fill-type="line"
8-
/>
3+
<oc-icon v-oc-tooltip="expirationDateTooltip" name="calendar-event" fill-type="line" />
94
<span class="oc-invisible-sr" v-text="screenreaderShareExpiration" />
105
</div>
116
</template>

packages/web-app-files/src/components/SideBar/Shares/Links/ListItem.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
class="oc-files-file-link-has-password oc-mr-xs"
3535
fill-type="line"
3636
:aria-label="$gettext('This link is password-protected')"
37+
role="img"
3738
/>
3839
</div>
3940
<expiration-date-indicator

packages/web-pkg/src/components/ContextActions/ActionMenuItem.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
:type="componentType"
66
v-bind="componentProps"
77
:class="[action.class, 'action-menu-item', 'oc-py-s', 'oc-px-m', 'oc-width-1-1']"
8-
:aria-label="componentProps.disabled ? action.disabledTooltip?.(actionOptions) : ''"
8+
:aria-label="componentProps.disabled ? action.disabledTooltip?.(actionOptions) : null"
99
data-testid="action-handler"
1010
:size="size"
1111
justify-content="left"
12+
:title="action.label(actionOptions)"
1213
v-on="componentListeners"
1314
>
1415
<oc-img

packages/web-pkg/src/components/CreateLinkModal.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@
5454
appearance="filled"
5555
variation="primary"
5656
:disabled="confirmButtonDisabled"
57+
:title="
58+
$pgettext(
59+
'Create link modal confirmation dropdown button title',
60+
'Additional copy options'
61+
)
62+
"
5763
>
5864
<oc-icon size="small" name="arrow-down-s" />
5965
</oc-button>

0 commit comments

Comments
 (0)