diff --git a/.drone.star b/.drone.star index 8c9bc8dfd99..92ee29c2bc7 100644 --- a/.drone.star +++ b/.drone.star @@ -571,6 +571,7 @@ def e2eTestsOnPlaywright(ctx): environment = { "BASE_URL_OCIS": "ocis:9200", "PLAYWRIGHT_BROWSERS_PATH": ".playwright", + "TESTS_RUNNER": "playwright", } steps += restoreBuildArtifactCache(ctx, "pnpm", ".pnpm-store") + \ @@ -594,6 +595,9 @@ def e2eTestsOnPlaywright(ctx): ], }] + if not "skip-a11y" in ctx.build.title.lower(): + steps += uploadA11yResult(ctx) + pipelines.append({ "kind": "pipeline", "type": "docker", @@ -637,6 +641,7 @@ def e2eTests(ctx): "federationServer": False, "failOnUncaughtConsoleError": "false", "extraServerEnvironment": {}, + "skipA11y": "false", } e2e_trigger = { @@ -671,6 +676,9 @@ def e2eTests(ctx): if ("with-tracing" in ctx.build.title.lower()): params["reportTracing"] = "true" + if "skip-a11y" in ctx.build.title.lower(): + params["skipA11y"] = "true" + environment = { "HEADLESS": "true", "RETRY": "1", @@ -680,6 +688,7 @@ def e2eTests(ctx): "PLAYWRIGHT_BROWSERS_PATH": ".playwright", "BROWSER": "chromium", "FEDERATED_BASE_URL_OCIS": "federation-ocis:9200", + "SKIP_A11Y_TESTS": params["skipA11y"], } steps += restoreBuildArtifactCache(ctx, "pnpm", ".pnpm-store") + \ @@ -2009,3 +2018,31 @@ def restoreBrowsersCache(): ], }, ] + +def uploadA11yResult(ctx): + return [ + { + "name": "upload-a11y-result", + "image": PLUGINS_S3_IMAGE, + "pull": "if-not-exists", + "settings": { + "bucket": S3_PUBLIC_CACHE_BUCKET, + "endpoint": S3_CACHE_SERVER, + "path_style": True, + "source": "%s/reports/e2e/a11y-report.json" % dir["web"], + "strip_prefix": "%s/reports/e2e/" % dir["web"], + "target": "/${DRONE_REPO}/${DRONE_BUILD_NUMBER}/a11y", + }, + "environment": { + "AWS_ACCESS_KEY_ID": { + "from_secret": "cache_public_s3_access_key", + }, + "AWS_SECRET_ACCESS_KEY": { + "from_secret": "cache_public_s3_secret_key", + }, + }, + "when": { + "status": ["failure", "success"], + }, + }, + ] diff --git a/docs/testing/accesibility.md b/docs/testing/accesibility.md new file mode 100644 index 00000000000..98c60235653 --- /dev/null +++ b/docs/testing/accesibility.md @@ -0,0 +1,48 @@ +--- +title: 'Accessibility' +date: 2025-11-10T00:00:00+00:00 +weight: 60 +geekdocRepo: https://github.com/owncloud/web +geekdocEditPath: edit/master/docs/testing +geekdocFilePath: accessibility.md +--- + +{{< toc >}} + +## Introduction + +Accessibility is a crucial aspect of web development. It ensures that web applications are usable by everyone, including people with disabilities. + +## What Tools We Use + +### eslint-plugin-vuejs-accessibility + +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). + +### @axe-core/playwright + +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. + +#### Running Accessibility Tests + +To run the accessibility tests, you can simply run our existing E2E tests using the following command: + +```bash +pnpm test:e2e:cucumber +``` + +#### Skipping Accessibility Tests Locally + +If you want to skip the accessibility tests, you can add the `SKIP_A11Y_TESTS` environment variable to your command. + +```bash +SKIP_A11Y_TESTS=true pnpm test:e2e:cucumber +``` + +#### Skipping Accessibility Tests in CI + +If you want to skip the accessibility tests in CI, you can add the `[skip-a11y]` flag into the title of the PR. + +#### Accessibility Report + +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. diff --git a/package.json b/package.json index 29ad083dd01..291397a9b65 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@vue/compiler-dom": "3.5.24", "@vue/compiler-sfc": "3.5.24", "@vue/test-utils": "2.4.6", + "axe-core": "^4.11.0", "browserslist-to-esbuild": "^2.1.1", "browserslist-useragent-regexp": "^4.1.3", "commander": "14.0.2", diff --git a/packages/design-system/src/components/OcLoader/OcLoader.spec.ts b/packages/design-system/src/components/OcLoader/OcLoader.spec.ts index 8a9fe673e53..018a7d7a909 100644 --- a/packages/design-system/src/components/OcLoader/OcLoader.spec.ts +++ b/packages/design-system/src/components/OcLoader/OcLoader.spec.ts @@ -1,6 +1,10 @@ import OcLoader from './OcLoader.vue' import { mount } from '@ownclouders/web-test-helpers' +const selectors = { + label: '[data-testid="oc-loader-label"]' +} + describe('OcLoader', () => { function getWrapper(props = {}) { return mount(OcLoader, { @@ -9,7 +13,7 @@ describe('OcLoader', () => { } it('should set provided aria-label', () => { const wrapper = getWrapper({ ariaLabel: 'test' }) - expect(wrapper.attributes('aria-label')).toBe('test') + expect(wrapper.find(selectors.label).text()).toBe('test') }) describe('when prop flat is enabled', () => { it('should set loader flat class to the wrapper', () => { diff --git a/packages/design-system/src/components/OcLoader/OcLoader.vue b/packages/design-system/src/components/OcLoader/OcLoader.vue index 5d2bde3097a..413e92dde74 100644 --- a/packages/design-system/src/components/OcLoader/OcLoader.vue +++ b/packages/design-system/src/components/OcLoader/OcLoader.vue @@ -1,5 +1,7 @@