├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── plan-release.yml │ └── publish.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── .prettierignore ├── .prettierrc.js ├── .release-plan.json ├── .template-lintrc.js ├── .watchmanconfig ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── RELEASE.md ├── addon-test-support ├── audit.ts ├── cli-options.ts ├── format-violation.ts ├── index.ts ├── logger.ts ├── performance.ts ├── reporter.ts ├── run-options.ts ├── setup-console-logger.ts ├── setup-global-a11y-hooks.ts ├── setup-middleware-reporter.ts ├── setup-qunit.ts ├── should-force-audit.ts ├── types │ └── index.d.ts └── use-middleware-reporter.ts ├── addon └── .gitkeep ├── app └── .gitkeep ├── cli-options-filter.js ├── config ├── ember-try-typescript.js ├── ember-try.js └── environment.js ├── content-for └── test-head-footer.html ├── docs └── assets │ ├── ember-a11y-testing-console-reporter.png │ ├── violation-console-output.png │ ├── violation-level-1.png │ ├── violation-level-2.png │ ├── violation-level-3.png │ ├── violation-log-output.png │ ├── violation-replaced-bg-element.png │ └── violation-styling.png ├── ember-cli-build.js ├── index.js ├── node-tests ├── fixtures │ └── violations.json └── setup-middleware-test.js ├── package.json ├── pnpm-lock.yaml ├── testem.js ├── tests ├── acceptance │ ├── a11y-audit-test.ts │ ├── reporter-test.ts │ ├── setup-global-a11y-hooks-test.ts │ └── setup-middleware-reporter-test.ts ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── axe-component.hbs │ │ │ ├── passing-component.hbs │ │ │ ├── violations-grid-item.hbs │ │ │ ├── violations-grid-item.js │ │ │ ├── x-paragraph.hbs │ │ │ └── x-paragraph.js │ │ ├── config │ │ │ └── environment.d.ts │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── resolver.js │ │ ├── router.js │ │ ├── routes │ │ │ ├── .gitkeep │ │ │ ├── ignored-image-alt.js │ │ │ ├── index.js │ │ │ └── violations.js │ │ ├── styles │ │ │ ├── app.css │ │ │ ├── app.scss │ │ │ ├── components │ │ │ │ ├── _button.scss │ │ │ │ ├── _components.scss │ │ │ │ ├── _image.scss │ │ │ │ ├── _menu-link-item.scss │ │ │ │ ├── _radio-track.scss │ │ │ │ └── _violations-grid-item.scss │ │ │ ├── globals.scss │ │ │ ├── object-patterns │ │ │ │ ├── _content-box.scss │ │ │ │ ├── _menu-link.scss │ │ │ │ └── _object-patterns.scss │ │ │ ├── pages │ │ │ │ ├── _pages.scss │ │ │ │ └── _violations.scss │ │ │ ├── typography │ │ │ │ ├── _fonts.scss │ │ │ │ ├── _headings.scss │ │ │ │ └── _typography.scss │ │ │ ├── utilities │ │ │ │ └── _utilities.scss │ │ │ └── variables │ │ │ │ └── _variables.scss │ │ └── templates │ │ │ ├── application.hbs │ │ │ ├── ignored-image-alt.hbs │ │ │ └── violations.hbs │ ├── config │ │ ├── ember-cli-update.json │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ ├── assets │ │ └── img │ │ │ └── empire-state-building-moonlight.png │ │ └── robots.txt ├── fixtures │ └── violations.json ├── helpers │ └── .gitkeep ├── index.html ├── integration │ ├── .gitkeep │ ├── components │ │ └── setup-global-a11y-hooks-for-render-test.gts │ └── helpers │ │ └── a11y-audit-test.gts ├── test-helper.js └── unit │ ├── .gitkeep │ ├── audit-test.ts │ ├── calculate-updated-href-test.ts │ ├── format-violation-test.ts │ └── get-current-route-name-test.ts ├── tsconfig.json └── types ├── dummy └── index.d.ts ├── ember-debug.d.ts ├── ember-get-config.d.ts └── global.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .*/ 17 | .eslintcache 18 | 19 | # ember-try 20 | /.node_modules.ember-try/ 21 | /bower.json.ember-try 22 | /npm-shrinkwrap.json.ember-try 23 | /package.json.ember-try 24 | /package-lock.json.ember-try 25 | /yarn.lock.ember-try 26 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | parserOptions: { 5 | ecmaVersion: 2018, 6 | sourceType: 'module', 7 | ecmaFeatures: { 8 | legacyDecorators: true, 9 | }, 10 | }, 11 | plugins: ['ember'], 12 | extends: [ 13 | 'eslint:recommended', 14 | 'plugin:ember/recommended', 15 | 'plugin:prettier/recommended', 16 | ], 17 | env: { 18 | browser: true, 19 | }, 20 | globals: { 21 | axe: false, 22 | }, 23 | rules: {}, 24 | overrides: [ 25 | // node files 26 | { 27 | files: [ 28 | './.eslintrc.js', 29 | './.prettierrc.js', 30 | './.template-lintrc.js', 31 | './cli-options-filter.js', 32 | './ember-cli-build.js', 33 | './index.js', 34 | './testem.js', 35 | './blueprints/*/index.js', 36 | './config/**/*.js', 37 | './node-tests/**/*.js', 38 | './tests/dummy/config/**/*.js', 39 | ], 40 | parserOptions: { 41 | sourceType: 'script', 42 | }, 43 | env: { 44 | browser: false, 45 | node: true, 46 | }, 47 | plugins: ['node'], 48 | extends: ['plugin:node/recommended'], 49 | }, 50 | { 51 | // typescript files 52 | files: ['**/*.ts'], 53 | parser: '@typescript-eslint/parser', 54 | plugins: ['@typescript-eslint'], 55 | rules: { 56 | 'no-unused-vars': 'off', 57 | '@typescript-eslint/no-unused-vars': 'error', 58 | 'no-redeclare': 'off', 59 | '@typescript-eslint/no-redeclare': ['error'], 60 | }, 61 | }, 62 | { 63 | // test files 64 | files: ['tests/**/*-test.{js,ts}'], 65 | extends: ['plugin:qunit/recommended'], 66 | }, 67 | ], 68 | }; 69 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: '/' 5 | schedule: 6 | interval: weekly 7 | time: '07:00' 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | - 'v*' 9 | pull_request: {} 10 | workflow_dispatch: 11 | 12 | concurrency: 13 | group: ci-${{ github.head_ref || github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | test: 18 | name: 'Tests' 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: pnpm/action-setup@v4 24 | - uses: actions/setup-node@v4 25 | with: 26 | node-version: 18 27 | cache: pnpm 28 | - name: Install Dependencies 29 | run: pnpm install 30 | - name: Lint 31 | run: pnpm lint 32 | - name: Run Ember Tests 33 | run: pnpm test:ember 34 | - name: Run Node Tests 35 | run: pnpm test:node 36 | 37 | floating: 38 | name: 'Floating Dependencies' 39 | runs-on: ubuntu-latest 40 | 41 | steps: 42 | - uses: actions/checkout@v4 43 | - uses: pnpm/action-setup@v4 44 | - uses: actions/setup-node@v4 45 | with: 46 | node-version: 18 47 | cache: pnpm 48 | - name: Install Dependencies 49 | run: pnpm install --no-lockfile 50 | - name: Run Tests 51 | run: pnpm test:ember 52 | - name: Run Node Tests 53 | run: pnpm test:node 54 | 55 | try-scenarios: 56 | name: ${{ matrix.try-scenario }} 57 | timeout-minutes: 15 58 | runs-on: ubuntu-latest 59 | needs: 'test' 60 | 61 | strategy: 62 | fail-fast: false 63 | matrix: 64 | try-scenario: 65 | - ember-lts-4.4 66 | - ember-lts-4.8 67 | - ember-lts-4.12 68 | - ember-release 69 | - ember-beta 70 | - ember-canary 71 | - embroider-safe 72 | - embroider-optimized 73 | 74 | steps: 75 | - uses: actions/checkout@v4 76 | - uses: pnpm/action-setup@v4 77 | - uses: actions/setup-node@v4 78 | with: 79 | node-version: 18 80 | cache: pnpm 81 | - name: Install Dependencies 82 | run: pnpm install 83 | - name: Run Tests 84 | run: ./node_modules/.bin/ember try:one ${{ matrix.try-scenario }} 85 | 86 | typescript-compatibility: 87 | name: Type checking - ${{ matrix.typescript-scenario }} 88 | runs-on: ubuntu-latest 89 | needs: 'test' 90 | 91 | strategy: 92 | fail-fast: false 93 | matrix: 94 | typescript-scenario: 95 | - typescript-4.2 96 | - typescript-4.3 97 | - typescript-4.4 98 | - typescript-4.5 99 | - typescript-next 100 | 101 | steps: 102 | - uses: actions/checkout@v4 103 | - uses: pnpm/action-setup@v4 104 | - uses: actions/setup-node@v4 105 | with: 106 | node-version: 18 107 | cache: pnpm 108 | - name: Install Dependencies 109 | run: pnpm install 110 | - name: Type checking 111 | run: ./node_modules/.bin/ember try:one --config-path="./config/ember-try-typescript.js" ${{ matrix.typescript-scenario }} 112 | -------------------------------------------------------------------------------- /.github/workflows/plan-release.yml: -------------------------------------------------------------------------------- 1 | name: Plan Release 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request_target: # This workflow has permissions on the repo, do NOT run code from PRs in this workflow. See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ 9 | types: 10 | - labeled 11 | - unlabeled 12 | 13 | concurrency: 14 | group: plan-release # only the latest one of these should ever be running 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | is-this-a-release: 19 | name: "Is this a release?" 20 | runs-on: ubuntu-latest 21 | outputs: 22 | command: ${{ steps.check-release.outputs.command }} 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 2 28 | ref: 'main' 29 | # This will only cause the `is-this-a-release` job to have a "command" of `release` 30 | # when the .release-plan.json file was changed on the last commit. 31 | - id: check-release 32 | run: if git diff --name-only HEAD HEAD~1 | grep -w -q ".release-plan.json"; then echo "command=release"; fi >> $GITHUB_OUTPUT 33 | 34 | create-prepare-release-pr: 35 | name: Create Prepare Release PR 36 | runs-on: ubuntu-latest 37 | timeout-minutes: 5 38 | needs: is-this-a-release 39 | permissions: 40 | contents: write 41 | issues: read 42 | pull-requests: write 43 | # only run on push event or workflow dispatch if plan wasn't updated (don't create a release plan when we're releasing) 44 | # only run on labeled event if the PR has already been merged 45 | if: ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && needs.is-this-a-release.outputs.command != 'release') || (github.event_name == 'pull_request_target' && github.event.pull_request.merged == true) 46 | 47 | steps: 48 | - uses: actions/checkout@v4 49 | # We need to download lots of history so that 50 | # github-changelog can discover what's changed since the last release 51 | with: 52 | fetch-depth: 0 53 | ref: 'main' 54 | - uses: pnpm/action-setup@v4 55 | - uses: actions/setup-node@v4 56 | with: 57 | node-version: 18 58 | cache: pnpm 59 | - run: pnpm install --frozen-lockfile 60 | - name: "Generate Explanation and Prep Changelogs" 61 | id: explanation 62 | run: | 63 | set +e 64 | pnpm release-plan prepare 2> >(tee -a release-plan-stderr.txt >&2) 65 | 66 | if [ $? -ne 0 ]; then 67 | release_plan_output=$(cat release-plan-stderr.txt) 68 | else 69 | release_plan_output=$(jq .description .release-plan.json -r) 70 | rm release-plan-stderr.txt 71 | 72 | if [ $(jq '.solution | length' .release-plan.json) -eq 1 ]; then 73 | new_version=$(jq -r '.solution[].newVersion' .release-plan.json) 74 | echo "new_version=v$new_version" >> $GITHUB_OUTPUT 75 | fi 76 | fi 77 | echo 'text<> $GITHUB_OUTPUT 78 | echo "$release_plan_output" >> $GITHUB_OUTPUT 79 | echo 'EOF' >> $GITHUB_OUTPUT 80 | env: 81 | GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }} 82 | 83 | - uses: peter-evans/create-pull-request@v7 84 | with: 85 | commit-message: "Prepare Release ${{ steps.explanation.outputs.new_version}} using 'release-plan'" 86 | labels: "internal" 87 | branch: release-preview 88 | title: Prepare Release ${{ steps.explanation.outputs.new_version }} 89 | body: | 90 | This PR is a preview of the release that [release-plan](https://github.com/embroider-build/release-plan) has prepared. To release you should just merge this PR 👍 91 | 92 | ----------------------------------------- 93 | 94 | ${{ steps.explanation.outputs.text }} 95 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # For every push to the primary branch with .release-plan.json modified, 2 | # runs release-plan. 3 | 4 | name: Publish Stable 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | branches: 10 | - main 11 | - master 12 | paths: 13 | - '.release-plan.json' 14 | 15 | concurrency: 16 | group: publish-${{ github.head_ref || github.ref }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | publish: 21 | name: "NPM Publish" 22 | runs-on: ubuntu-latest 23 | permissions: 24 | contents: write 25 | pull-requests: write 26 | id-token: write 27 | attestations: write 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: pnpm/action-setup@v4 32 | - uses: actions/setup-node@v4 33 | with: 34 | node-version: 18 35 | # This creates an .npmrc that reads the NODE_AUTH_TOKEN environment variable 36 | registry-url: 'https://registry.npmjs.org' 37 | cache: pnpm 38 | - run: pnpm install --frozen-lockfile 39 | - name: Publish to NPM 40 | run: NPM_CONFIG_PROVENANCE=true pnpm release-plan publish 41 | env: 42 | GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }} 43 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | /.env* 13 | /.pnp* 14 | /.sass-cache 15 | /.eslintcache 16 | /connect.lock 17 | /coverage/ 18 | /libpeerconnection.log 19 | /npm-debug.log* 20 | /testem.log 21 | /yarn-error.log 22 | 23 | # ember-try 24 | /.node_modules.ember-try/ 25 | /bower.json.ember-try 26 | /npm-shrinkwrap.json.ember-try 27 | /package.json.ember-try 28 | /package-lock.json.ember-try 29 | /yarn.lock.ember-try 30 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /bower_components/ 7 | 8 | # misc 9 | /.bowerrc 10 | /.editorconfig 11 | /.ember-cli 12 | /.env* 13 | /.eslintcache 14 | /.eslintignore 15 | /.eslintrc.js 16 | /.git/ 17 | /.github/ 18 | /.gitignore 19 | /.prettierignore 20 | /.prettierrc.js 21 | /.template-lintrc.js 22 | /.travis.yml 23 | /.watchmanconfig 24 | /bower.json 25 | /config/ember-try.js 26 | /CONTRIBUTING.md 27 | /ember-cli-build.js 28 | /testem.js 29 | /tests/ 30 | /node-tests/ 31 | /yarn-error.log 32 | /yarn.lock 33 | .gitkeep 34 | 35 | # ember-try 36 | /.node_modules.ember-try/ 37 | /bower.json.ember-try 38 | /npm-shrinkwrap.json.ember-try 39 | /package.json.ember-try 40 | /package-lock.json.ember-try 41 | /yarn.lock.ember-try 42 | 43 | # custom 44 | config/ember-try-typescript.js 45 | /docs/ 46 | /RELEASE.md 47 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | use-node-version=18.19.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .eslintcache 17 | .lint-todo/ 18 | 19 | # ember-try 20 | /.node_modules.ember-try/ 21 | /bower.json.ember-try 22 | /npm-shrinkwrap.json.ember-try 23 | /package.json.ember-try 24 | /package-lock.json.ember-try 25 | /yarn.lock.ember-try 26 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | }; 6 | -------------------------------------------------------------------------------- /.release-plan.json: -------------------------------------------------------------------------------- 1 | { 2 | "solution": { 3 | "ember-a11y-testing": { 4 | "impact": "patch", 5 | "oldVersion": "7.1.1", 6 | "newVersion": "7.1.2", 7 | "tagName": "latest", 8 | "constraints": [ 9 | { 10 | "impact": "patch", 11 | "reason": "Appears in changelog section :memo: Documentation" 12 | }, 13 | { 14 | "impact": "patch", 15 | "reason": "Appears in changelog section :house: Internal" 16 | } 17 | ], 18 | "pkgJSONPath": "./package.json" 19 | } 20 | }, 21 | "description": "## Release (2025-04-09)\n\n* ember-a11y-testing 7.1.2 (patch)\n\n#### :memo: Documentation\n* `ember-a11y-testing`\n * [#594](https://github.com/ember-a11y/ember-a11y-testing/pull/594) [documentation] update changelog for v7.1.1 ([@MelSumner](https://github.com/MelSumner))\n\n#### :house: Internal\n* `ember-a11y-testing`\n * [#600](https://github.com/ember-a11y/ember-a11y-testing/pull/600) [internal] adding 4.4 try scenario back in ([@MelSumner](https://github.com/MelSumner))\n * [#596](https://github.com/ember-a11y/ember-a11y-testing/pull/596) [internal] tidy up ([@MelSumner](https://github.com/MelSumner))\n * [#598](https://github.com/ember-a11y/ember-a11y-testing/pull/598) [internal] set up release-plan ([@mansona](https://github.com/mansona))\n * [#597](https://github.com/ember-a11y/ember-a11y-testing/pull/597) [internal] move from yarn to pnpm ([@mansona](https://github.com/mansona))\n\n#### Committers: 2\n- Chris Manson ([@mansona](https://github.com/mansona))\n- Melanie Sumner ([@MelSumner](https://github.com/MelSumner))\n" 22 | } 23 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | }; 6 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Release (2025-04-09) 4 | 5 | * ember-a11y-testing 7.1.2 (patch) 6 | 7 | #### :memo: Documentation 8 | * `ember-a11y-testing` 9 | * [#594](https://github.com/ember-a11y/ember-a11y-testing/pull/594) [documentation] update changelog for v7.1.1 ([@MelSumner](https://github.com/MelSumner)) 10 | 11 | #### :house: Internal 12 | * `ember-a11y-testing` 13 | * [#600](https://github.com/ember-a11y/ember-a11y-testing/pull/600) [internal] adding 4.4 try scenario back in ([@MelSumner](https://github.com/MelSumner)) 14 | * [#596](https://github.com/ember-a11y/ember-a11y-testing/pull/596) [internal] tidy up ([@MelSumner](https://github.com/MelSumner)) 15 | * [#598](https://github.com/ember-a11y/ember-a11y-testing/pull/598) [internal] set up release-plan ([@mansona](https://github.com/mansona)) 16 | * [#597](https://github.com/ember-a11y/ember-a11y-testing/pull/597) [internal] move from yarn to pnpm ([@mansona](https://github.com/mansona)) 17 | 18 | #### Committers: 2 19 | - Chris Manson ([@mansona](https://github.com/mansona)) 20 | - Melanie Sumner ([@MelSumner](https://github.com/MelSumner)) 21 | 22 | ## v7.1.1 (2025-04-09) 23 | 24 | ### :house: Internal 25 | 26 | - Expand @ember/test-waiters dependency range to allow v4 (PR #583) 27 | 28 | ### Committers: 1 29 | 30 | - Sergey Astapov (@SergeAstapov) 31 | 32 | ## v7.1.0 (2025-02-20) 33 | 34 | ### :house: Internal 35 | 36 | - expand test-helpers peer range (PR #580) 37 | - tighten type imports (PR #573) 38 | - removed unused `body-parser` dependency (PR #572) 39 | - removed `ember-destroyable-polyfill` (no longer needed) (PR #571) 40 | - enable embroider tests in CI (PR #568) 41 | - move component templates into `app/components` to resolve `ember-beta` errors (PR #567) 42 | - update prettier (PR #565) 43 | - fix typescript context for integration tests (PR #559) 44 | 45 | ### Committers: 5 46 | 47 | - Chris Manson (@mansona) 48 | - David Baker (@acorncom) 49 | - Stanley Stuart (@fivetanley) 50 | - Dean Marano (@deanmarano) 51 | - Alex (@void-mAlex) 52 | 53 | 54 | ## v7.0.2 (2024-10-31) 55 | 56 | ### :bug: Bug Fix 57 | 58 | * fix typescript context for integration tests (7b9cfc9) 59 | * use Testem.afterTests instead of QUnit.done (8c7c604) 60 | 61 | ### Committers: 1 62 | - Stanley Stuart ([@fivetanley](https://github.com/fivetanley)) 63 | 64 | 65 | ## v7.0.1 (2024-09-26) 66 | 67 | Patch release to update packages per security policy. 68 | 69 | ## v7.0.0 (2024-08-27) 70 | 71 | #### :rocket: Enhancement 72 | * [#448](https://github.com/ember-a11y/ember-a11y-testing/pull/448) feat: add qunit config ([@GreatWizard](https://github.com/GreatWizard)) 73 | 74 | #### :house: Internal 75 | * [#516](https://github.com/ember-a11y/ember-a11y-testing/pull/516) Updates Andrew Lee's contributor email ([@drewlee](https://github.com/drewlee)) 76 | 77 | #### Committers: 4 78 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 79 | - David Baker ([@acorncom](https://github.com/acorncom)) 80 | - Guillaume Gérard ([@GreatWizard](https://github.com/GreatWizard)) 81 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 82 | 83 | ## v6.1.1 (2023-08-24) 84 | 85 | #### :bug: Bug Fix 86 | * [#514](https://github.com/ember-a11y/ember-a11y-testing/pull/514) Ensures context for current URL & route middleware reporting ([@drewlee](https://github.com/drewlee)) 87 | 88 | #### Committers: 1 89 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 90 | 91 | 92 | ## v6.1.0 (2023-06-21) 93 | 94 | #### :rocket: Enhancement 95 | * [#511](https://github.com/ember-a11y/ember-a11y-testing/pull/511) Disabling transitions and animations while running axe tests ([@tgvrssanthosh](https://github.com/tgvrssanthosh)) 96 | 97 | #### Committers: 1 98 | - Santhosh Venkata Rama Siva Thanakala Gani ([@tgvrssanthosh](https://github.com/tgvrssanthosh)) 99 | 100 | 101 | ## v6.0.0 (2023-06-21) 102 | 103 | #### :boom: Breaking Change 104 | * [#509](https://github.com/ember-a11y/ember-a11y-testing/pull/509) Upgrade @ember/test-helpers to 3.x ([@fivetanley](https://github.com/fivetanley)) 105 | 106 | #### :bug: Bug Fix 107 | * [#507](https://github.com/ember-a11y/ember-a11y-testing/pull/507) Update setup-middleware-reporter.ts ([@deanmarano](https://github.com/deanmarano)) 108 | * [#503](https://github.com/ember-a11y/ember-a11y-testing/pull/503) Fix type issues with v5.2.0 ([@robostheimer](https://github.com/robostheimer)) 109 | 110 | #### :house: Internal 111 | * [#508](https://github.com/ember-a11y/ember-a11y-testing/pull/508) fix ember-try for >= 5.x releases ([@fivetanley](https://github.com/fivetanley)) 112 | 113 | #### Committers: 3 114 | - Dean Marano ([@deanmarano](https://github.com/deanmarano)) 115 | - Rob Ostheimer ([@robostheimer](https://github.com/robostheimer)) 116 | - Stanley Stuart ([@fivetanley](https://github.com/fivetanley)) 117 | 118 | 119 | ## v6.0.0-0 (2023-06-13) 120 | 121 | #### :boom: Breaking Change 122 | * [#509](https://github.com/ember-a11y/ember-a11y-testing/pull/509) Upgrade @ember/test-helpers to 3.x ([@fivetanley](https://github.com/fivetanley)) 123 | 124 | #### :bug: Bug Fix 125 | * [#507](https://github.com/ember-a11y/ember-a11y-testing/pull/507) Update setup-middleware-reporter.ts ([@deanmarano](https://github.com/deanmarano)) 126 | 127 | #### :house: Internal 128 | * [#508](https://github.com/ember-a11y/ember-a11y-testing/pull/508) fix ember-try for >= 5.x releases ([@fivetanley](https://github.com/fivetanley)) 129 | 130 | #### Committers: 2 131 | - Dean Marano ([@deanmarano](https://github.com/deanmarano)) 132 | - Stanley Stuart ([@fivetanley](https://github.com/fivetanley)) 133 | 134 | 135 | ## v5.2.1 (2023-05-25) 136 | 137 | #### :bug: Bug Fix 138 | * [#503](https://github.com/ember-a11y/ember-a11y-testing/pull/503) Fix type issues with v5.2.0 ([@robostheimer](https://github.com/robostheimer)) 139 | 140 | #### Committers: 1 141 | - Rob Ostheimer ([@robostheimer](https://github.com/robostheimer)) 142 | 143 | 144 | ## v5.2.0 (2023-02-02) 145 | 146 | #### :bug: Bug Fix 147 | * [#491](https://github.com/ember-a11y/ember-a11y-testing/pull/491) Fixes currentRouteName middleware reporter test failures ([@drewlee](https://github.com/drewlee)) 148 | 149 | #### Committers: 1 150 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 151 | 152 | 153 | ## v5.1.0 (2022-12-03) 154 | 155 | #### :rocket: Enhancement 156 | * [#466](https://github.com/ember-a11y/ember-a11y-testing/pull/466) Bump axe-core from 4.3.5 to 4.5.2 ([@drewlee](https://github.com/drewlee)) 157 | 158 | #### :bug: Bug Fix 159 | * [#460](https://github.com/ember-a11y/ember-a11y-testing/pull/460) Fixes CI failures ([@drewlee](https://github.com/drewlee)) 160 | 161 | #### Committers: 1 162 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 163 | 164 | 165 | ## v5.0.0 (2021-12-28) 166 | 167 | #### :boom: Breaking Change 168 | * [#341](https://github.com/ember-a11y/ember-a11y-testing/pull/341) Update addon blueprint with ember-cli-update to 4.0.1 ([@SergeAstapov](https://github.com/SergeAstapov)) 169 | * [#325](https://github.com/ember-a11y/ember-a11y-testing/pull/325) Bump ember-auto-import to 2.2.4 and introduce webpack ([@SergeAstapov](https://github.com/SergeAstapov)) 170 | * [#327](https://github.com/ember-a11y/ember-a11y-testing/pull/327) Delete deprecated a11yAuditIf test helper ([@SergeAstapov](https://github.com/SergeAstapov)) 171 | * [#326](https://github.com/ember-a11y/ember-a11y-testing/pull/326) Drop Node.js 10 support ([@SergeAstapov](https://github.com/SergeAstapov)) 172 | 173 | #### :bug: Bug Fix 174 | * [#331](https://github.com/ember-a11y/ember-a11y-testing/pull/331) Properly re-export types ([@mydea](https://github.com/mydea)) 175 | 176 | #### :house: Internal 177 | * [#344](https://github.com/ember-a11y/ember-a11y-testing/pull/344) Add typechecking compat scenarios to CI ([@SergeAstapov](https://github.com/SergeAstapov)) 178 | * [#342](https://github.com/ember-a11y/ember-a11y-testing/pull/342) Introduce `eslint-plugin-qunit` per latest addon blueprint ([@SergeAstapov](https://github.com/SergeAstapov)) 179 | * [#345](https://github.com/ember-a11y/ember-a11y-testing/pull/345) Add `docs` folder and `RELEASE.md` file to npmignore ([@SergeAstapov](https://github.com/SergeAstapov)) 180 | * [#328](https://github.com/ember-a11y/ember-a11y-testing/pull/328) Update npmignore file ([@SergeAstapov](https://github.com/SergeAstapov)) 181 | 182 | #### Committers: 2 183 | - Francesco Novy ([@mydea](https://github.com/mydea)) 184 | - Sergey Astapov ([@SergeAstapov](https://github.com/SergeAstapov)) 185 | 186 | 187 | ## v4.3.0 (2021-11-11) 188 | 189 | #### :rocket: Enhancement 190 | * [#318](https://github.com/ember-a11y/ember-a11y-testing/pull/318) Bump axe-core 4.3.5 ([@snewcomer](https://github.com/snewcomer)) 191 | * [#319](https://github.com/ember-a11y/ember-a11y-testing/pull/319) Make helpers configurable in `setupGlobalA11yHooks` mode ([@jgwhite](https://github.com/jgwhite)) 192 | 193 | #### :house: Internal 194 | * [#322](https://github.com/ember-a11y/ember-a11y-testing/pull/322) Adds get-port to ensure we have an available port ([@scalvert](https://github.com/scalvert)) 195 | 196 | #### Committers: 3 197 | - Jamie White ([@jgwhite](https://github.com/jgwhite)) 198 | - Scott Newcomer ([@snewcomer](https://github.com/snewcomer)) 199 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 200 | 201 | 202 | ## v4.2.1 (2021-10-18) 203 | 204 | #### :bug: Bug Fix 205 | * [#298](https://github.com/ember-a11y/ember-a11y-testing/pull/298) Fixes duplicate identifier issue by ignoring type checking of .d.ts files in node_modules ([@scalvert](https://github.com/scalvert)) 206 | 207 | #### :house: Internal 208 | * [#309](https://github.com/ember-a11y/ember-a11y-testing/pull/309) Fixes SASS deprecation related to division ([@scalvert](https://github.com/scalvert)) 209 | * [#308](https://github.com/ember-a11y/ember-a11y-testing/pull/308) Adding octane edition to package.json ([@scalvert](https://github.com/scalvert)) 210 | 211 | #### Committers: 2 212 | - Francesco Novy ([@mydea](https://github.com/mydea)) 213 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 214 | 215 | 216 | ## v4.2.0 (2021-08-24) 217 | 218 | #### :bug: Bug Fix 219 | * [#289](https://github.com/ember-a11y/ember-a11y-testing/pull/289) Fixes deprecate call to include for and since props ([@scalvert](https://github.com/scalvert)) 220 | 221 | #### :house: Internal 222 | * [#288](https://github.com/ember-a11y/ember-a11y-testing/pull/288) Convert to using external middleware reporter package ([@scalvert](https://github.com/scalvert)) 223 | 224 | #### Committers: 1 225 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 226 | 227 | 228 | ## v4.1.2 (2021-07-01) 229 | 230 | #### :bug: Bug Fix 231 | * [#272](https://github.com/ember-a11y/ember-a11y-testing/pull/272) Fixes issue when violations count is reported to be 0 ([@scalvert](https://github.com/scalvert)) 232 | 233 | #### :house: Internal 234 | * [#271](https://github.com/ember-a11y/ember-a11y-testing/pull/271) Migrates to native dependabot ([@scalvert](https://github.com/scalvert)) 235 | 236 | #### Committers: 1 237 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 238 | 239 | 240 | ## v4.1.1 (2021-06-24) 241 | 242 | #### :bug: Bug Fix 243 | * [#270](https://github.com/ember-a11y/ember-a11y-testing/pull/270) Fixes URL mutation when setting the `enableA11yAudit` query parameter ([@drewlee](https://github.com/drewlee)) 244 | 245 | #### Committers: 1 246 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 247 | 248 | 249 | ## v4.1.0 (2021-06-18) 250 | 251 | #### :rocket: Enhancement 252 | * [#267](https://github.com/ember-a11y/ember-a11y-testing/pull/267) Provide conditional usage for 'setupMiddlewareReporter' ([@drewlee](https://github.com/drewlee)) 253 | 254 | #### Committers: 1 255 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 256 | 257 | 258 | ## v4.0.8 (2021-06-11) 259 | 260 | #### :bug: Bug Fix 261 | * [#268](https://github.com/ember-a11y/ember-a11y-testing/pull/268) Adjusting time of data gathering in middleware reporter ([@scalvert](https://github.com/scalvert)) 262 | * [#264](https://github.com/ember-a11y/ember-a11y-testing/pull/264) Fixes failing CI and crypto error ([@scalvert](https://github.com/scalvert)) 263 | 264 | #### :house: Internal 265 | * [#265](https://github.com/ember-a11y/ember-a11y-testing/pull/265) Update .npmignore ([@SergeAstapov](https://github.com/SergeAstapov)) 266 | * [#262](https://github.com/ember-a11y/ember-a11y-testing/pull/262) fix(deps): update dependency validate-peer-dependencies to v2 ([@renovate[bot]](https://github.com/apps/renovate)) 267 | * [#259](https://github.com/ember-a11y/ember-a11y-testing/pull/259) fix(deps): update dependency fs-extra to v10 ([@renovate[bot]](https://github.com/apps/renovate)) 268 | 269 | #### Committers: 2 270 | - Sergey Astapov ([@SergeAstapov](https://github.com/SergeAstapov)) 271 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 272 | 273 | 274 | ## v4.0.7 (2021-05-11) 275 | 276 | #### :bug: Bug Fix 277 | * [#261](https://github.com/ember-a11y/ember-a11y-testing/pull/261) Removes deprecated `content-for` styles ([@drewlee](https://github.com/drewlee)) 278 | 279 | #### Committers: 1 280 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 281 | 282 | 283 | ## v4.0.6 (2021-04-28) 284 | 285 | #### :bug: Bug Fix 286 | * [#258](https://github.com/ember-a11y/ember-a11y-testing/pull/258) Don't print to the console when there are no issues ([@nlfurniss](https://github.com/nlfurniss)) 287 | 288 | #### Committers: 1 289 | - Nathaniel Furniss ([@nlfurniss](https://github.com/nlfurniss)) 290 | 291 | 292 | ## v4.0.5 (2021-04-27) 293 | 294 | #### :house: Internal 295 | * [#256](https://github.com/ember-a11y/ember-a11y-testing/pull/256) Moves to using promise-based API for axe invocation ([@scalvert](https://github.com/scalvert)) 296 | 297 | #### Committers: 1 298 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 299 | 300 | 301 | ## v4.0.4 (2021-04-22) 302 | 303 | #### :house: Internal 304 | * [#230](https://github.com/ember-a11y/ember-a11y-testing/pull/230) chore(deps): update eslint (major) ([@renovate[bot]](https://github.com/apps/renovate)) 305 | * [#241](https://github.com/ember-a11y/ember-a11y-testing/pull/241) chore(deps): update dependency ember-template-lint to v3 ([@renovate[bot]](https://github.com/apps/renovate)) 306 | * [#255](https://github.com/ember-a11y/ember-a11y-testing/pull/255) Removes deprecated visual audit related components ([@drewlee](https://github.com/drewlee)) 307 | * [#254](https://github.com/ember-a11y/ember-a11y-testing/pull/254) fix(deps): update dependency date-and-time to v1 ([@renovate[bot]](https://github.com/apps/renovate)) 308 | * [#253](https://github.com/ember-a11y/ember-a11y-testing/pull/253) chore(deps): update dependency @types/ember__test-helpers to v2 ([@renovate[bot]](https://github.com/apps/renovate)) 309 | * [#250](https://github.com/ember-a11y/ember-a11y-testing/pull/250) chore(deps): update node.js to v12.22.1 ([@renovate[bot]](https://github.com/apps/renovate)) 310 | 311 | #### Committers: 1 312 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 313 | 314 | 315 | ## v4.0.3 (2021-04-16) 316 | 317 | #### :bug: Bug Fix 318 | * [#251](https://github.com/ember-a11y/ember-a11y-testing/pull/251) Removes unnecessary, legacy build code for inclusion of axe dependencies. ([@MelSumner](https://github.com/MelSumner)) 319 | 320 | #### :memo: Documentation 321 | * [#243](https://github.com/ember-a11y/ember-a11y-testing/pull/243) Correct link to axe-core API docs ([@danwenzel](https://github.com/danwenzel)) 322 | * [#240](https://github.com/ember-a11y/ember-a11y-testing/pull/240) Updating README to indicate the need to use query param with global setup helper ([@scalvert](https://github.com/scalvert)) 323 | * [#239](https://github.com/ember-a11y/ember-a11y-testing/pull/239) Correct import path of setRunOptions in README ([@danwenzel](https://github.com/danwenzel)) 324 | 325 | #### :house: Internal 326 | * [#244](https://github.com/ember-a11y/ember-a11y-testing/pull/244) chore(deps): update dependency @embroider/test-setup to ^0.39.0 ([@renovate[bot]](https://github.com/apps/renovate)) 327 | * [#247](https://github.com/ember-a11y/ember-a11y-testing/pull/247) Fixes CI build failures for missing module 'levenary' ([@drewlee](https://github.com/drewlee)) 328 | * [#236](https://github.com/ember-a11y/ember-a11y-testing/pull/236) Update Node.js to v12.21.0 ([@renovate[bot]](https://github.com/apps/renovate)) 329 | 330 | #### Committers: 4 331 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 332 | - Dan Wenzel ([@danwenzel](https://github.com/danwenzel)) 333 | - Melanie Sumner ([@MelSumner](https://github.com/MelSumner)) 334 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 335 | 336 | 337 | ## v4.0.2 (2021-02-25) 338 | 339 | #### :bug: Bug Fix 340 | * [#238](https://github.com/ember-a11y/ember-a11y-testing/pull/238) Fix testing container CSS overrides ([@drewlee](https://github.com/drewlee)) 341 | 342 | #### :house: Internal 343 | * [#238](https://github.com/ember-a11y/ember-a11y-testing/pull/238) Unpin ember-qunit ([@drewlee](https://github.com/drewlee)) 344 | 345 | #### Committers: 1 346 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 347 | 348 | 349 | ## v4.0.1 (2021-02-09) 350 | 351 | #### :bug: Bug Fix 352 | * [#233](https://github.com/ember-a11y/ember-a11y-testing/pull/233) Fix cannot find module `qunit` under embroider ([@thoov](https://github.com/thoov)) 353 | 354 | #### :memo: Documentation 355 | * [#229](https://github.com/ember-a11y/ember-a11y-testing/pull/229) Add info describing required peerDependency ([@scalvert](https://github.com/scalvert)) 356 | 357 | #### :house: Internal 358 | * [#234](https://github.com/ember-a11y/ember-a11y-testing/pull/234) Pin ember-qunit to 5.0.0. ([@rwjblue](https://github.com/rwjblue)) 359 | 360 | #### Committers: 3 361 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 362 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 363 | - Travis Hoover ([@thoov](https://github.com/thoov)) 364 | 365 | 366 | ## v4.0.0 (2020-11-03) 367 | 368 | #### :memo: Documentation 369 | * [#225](https://github.com/ember-a11y/ember-a11y-testing/pull/225) Adding contributors to package.json ([@scalvert](https://github.com/scalvert)) 370 | * [#221](https://github.com/ember-a11y/ember-a11y-testing/pull/221) fix **tiny** syntax error in readme code snippet ([@zelaznik](https://github.com/zelaznik)) 371 | 372 | #### :house: Internal 373 | * [#227](https://github.com/ember-a11y/ember-a11y-testing/pull/227) Upgrading @ember/test-helpers to 2.0.0 ([@scalvert](https://github.com/scalvert)) 374 | 375 | #### Committers: 2 376 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 377 | - Steve Zelaznik ([@zelaznik](https://github.com/zelaznik)) 378 | 379 | 380 | ## v4.0.0-beta.2 (2020-09-22) 381 | 382 | #### :rocket: Enhancement 383 | * [#217](https://github.com/ember-a11y/ember-a11y-testing/pull/217) Updating result schema to include more specific data ([@scalvert](https://github.com/scalvert)) 384 | 385 | #### :memo: Documentation 386 | * [#215](https://github.com/ember-a11y/ember-a11y-testing/pull/215) Updates the README and ensures correct jsdocs are present for all public APIs ([@scalvert](https://github.com/scalvert)) 387 | 388 | #### :house: Internal 389 | * [#218](https://github.com/ember-a11y/ember-a11y-testing/pull/218) Downgrading ember-cli-typescript to 3.0.0 for embroider compatability ([@scalvert](https://github.com/scalvert)) 390 | * [#219](https://github.com/ember-a11y/ember-a11y-testing/pull/219) Update ember-qunit to latest beta. ([@rwjblue](https://github.com/rwjblue)) 391 | * [#216](https://github.com/ember-a11y/ember-a11y-testing/pull/216) chore(deps): update dependency node-fetch to v2.6.1 [security] ([@renovate[bot]](https://github.com/apps/renovate)) 392 | 393 | #### Committers: 2 394 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 395 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 396 | 397 | ## v4.0.0-beta.1 (2020-09-11) 398 | 399 | #### :boom: Breaking Change 400 | * [#205](https://github.com/ember-a11y/ember-a11y-testing/pull/205) Remove audit components in favor of industry standard tools ([@scalvert](https://github.com/scalvert)) 401 | 402 | #### :rocket: Enhancement 403 | * [#212](https://github.com/ember-a11y/ember-a11y-testing/pull/212) Adding ability to post violations via middleware ([@scalvert](https://github.com/scalvert)) 404 | * [#210](https://github.com/ember-a11y/ember-a11y-testing/pull/210) Adds custom reporter capability ([@scalvert](https://github.com/scalvert)) 405 | 406 | #### :house: Internal 407 | * [#213](https://github.com/ember-a11y/ember-a11y-testing/pull/213) Update dependency eslint-plugin-ember to v9 ([@renovate[bot]](https://github.com/apps/renovate)) 408 | * [#211](https://github.com/ember-a11y/ember-a11y-testing/pull/211) Update dependency release-it to v14 ([@renovate[bot]](https://github.com/apps/renovate)) 409 | * [#209](https://github.com/ember-a11y/ember-a11y-testing/pull/209) Update dependency ember-cli-typescript to v4 ([@renovate[bot]](https://github.com/apps/renovate)) 410 | * [#206](https://github.com/ember-a11y/ember-a11y-testing/pull/206) Update eslint to v4 (major) ([@renovate[bot]](https://github.com/apps/renovate)) 411 | * [#194](https://github.com/ember-a11y/ember-a11y-testing/pull/194) Update dependency typescript to v4 ([@renovate[bot]](https://github.com/apps/renovate)) 412 | 413 | #### Committers: 1 414 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 415 | 416 | 417 | ## v4.0.0-beta.0 (2020-08-31) 418 | 419 | #### :boom: Breaking Change 420 | * [#199](https://github.com/ember-a11y/ember-a11y-testing/pull/199) Drop support for `moduleForAcceptance` ([@scalvert](https://github.com/scalvert)) 421 | * [#175](https://github.com/ember-a11y/ember-a11y-testing/pull/175) Update dependency axe-core to v4 ([@renovate[bot]](https://github.com/apps/renovate)) 422 | 423 | #### :rocket: Enhancement 424 | * [#203](https://github.com/ember-a11y/ember-a11y-testing/pull/203) Deprecate `a11yAuditIf` ([@scalvert](https://github.com/scalvert)) 425 | * [#200](https://github.com/ember-a11y/ember-a11y-testing/pull/200) Implements `setupGlobalA11yHooks` helper ([@scalvert](https://github.com/scalvert)) 426 | * [#191](https://github.com/ember-a11y/ember-a11y-testing/pull/191) Converts files in `addon-test-support` to TypeScript ([@scalvert](https://github.com/scalvert)) 427 | 428 | #### :bug: Bug Fix 429 | * [#182](https://github.com/ember-a11y/ember-a11y-testing/pull/182) Unpinning dependencies to allow for easier blueprint upgrades ([@scalvert](https://github.com/scalvert)) 430 | 431 | #### :house: Internal 432 | * [#204](https://github.com/ember-a11y/ember-a11y-testing/pull/204) Converting utils to TS to fix compile time errors ([@scalvert](https://github.com/scalvert)) 433 | * [#193](https://github.com/ember-a11y/ember-a11y-testing/pull/193) Updates build status badge to use github actions ([@scalvert](https://github.com/scalvert)) 434 | * [#192](https://github.com/ember-a11y/ember-a11y-testing/pull/192) Convert to github actions ([@scalvert](https://github.com/scalvert)) 435 | * [#188](https://github.com/ember-a11y/ember-a11y-testing/pull/188) Update linting configuration to work with TypeScript (+Prettier) ([@scalvert](https://github.com/scalvert)) 436 | * [#184](https://github.com/ember-a11y/ember-a11y-testing/pull/184) Add support for migrating to TypeScript ([@scalvert](https://github.com/scalvert)) 437 | * [#183](https://github.com/ember-a11y/ember-a11y-testing/pull/183) Setup automated release system. ([@rwjblue](https://github.com/rwjblue)) 438 | * [#181](https://github.com/ember-a11y/ember-a11y-testing/pull/181) Regenerates lockfile to address issue with fsevents version ([@scalvert](https://github.com/scalvert)) 439 | 440 | #### Committers: 3 441 | - Andrew A Lee ([@drewlee](https://github.com/drewlee)) 442 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 443 | - Steve Calvert ([@scalvert](https://github.com/scalvert)) 444 | 445 | 446 | # 3.0.2 / 2020-06-29 447 | 448 | - Update dependency ember-cli-htmlbars to v5.2.0 449 | - Update dependency eslint-plugin-ember to v8.9.0 450 | - Update dependency sass to v1.26.9 451 | - Update ember packages 452 | - Update dependency axe-core to v3.5.5 453 | - Update dependency eslint-plugin-ember to v8.7.0 454 | 455 | # 3.0.1 / 2020-05-18 456 | 457 | - Update dependency ember-cli-version-checker to v5.1.1 458 | - Update dependency eslint-plugin-ember to v8.5.1 459 | - Update ember packages 460 | 461 | # 3.0.0 / 2020-05-07 462 | 463 | - Drops support for Ember 2.18 due to major dependencies 464 | - Drops support for Node.js v8 due to EOL 465 | - Update eslint 466 | - Update dependency ember-cli-version-checker to v5 467 | - Update dependency @ember/optional-features to v1 468 | - Update ember packages 469 | - Update sass 470 | - Update dependency ember-cli-github-pages to v0.2.2 471 | - Update dependency axe-core to v3.5.3 472 | - Update dependency ember-radio-button to v2 473 | 474 | # 2.0.0 / 2019-11-21 475 | 476 | - Upgrade Ember CLI to 3.12 LTS and drop Node.js 6 support ([#138](https://github.com/ember-a11y/ember-a11y-testing/pull/138)) 477 | 478 | # 1.1.1 / 2019-09-29 479 | 480 | - Fixes code included in production builds [#108](<[#134](https://github.com/ember-a11y/ember-a11y-testing/pull/134)>) 481 | 482 | # 1.1.0 / 2019-09-09 483 | 484 | - Bumps axe-core to v3.3 485 | - Adds concurrent-axe strict check to counter next() 0 value 486 | - Fixes [#124](https://github.com/ember-a11y/ember-a11y-testing/issues/124), allows a11yAudit to account for axe inlude/exclude context param 487 | - Fixes [#127](https://github.com/ember-a11y/ember-a11y-testing/issues/127), removes use of Ember.Logger 488 | - Fixes [#107](https://github.com/ember-a11y/ember-a11y-testing/issues/107) by adding app re-exports 489 | 490 | # 0.5.7 / 2019-03-08 491 | 492 | - Axe-core version bump to 3.2.2 493 | 494 | # 0.5.6 / 2018-11-22 495 | 496 | - Adds user timing mechanism for performance profiling 497 | - Use process.env.EMBER_ENV for env check 498 | 499 | # 0.5.5 / 2018-11-04 500 | 501 | - Fixes #113: provides empty object if axeOptions are not defined to prevent axe.run from failing 502 | 503 | # 0.5.4 / 2018-10-22 504 | 505 | - Fix services for engines, add option to exclude axe-core for component audits 506 | 507 | # 0.5.3 / 2018-10-01 508 | 509 | - Update axe-core to 3.1.2 ([#109](https://github.com/ember-a11y/ember-a11y-testing/pull/109)) 510 | - Make aXe v3 compatible with component audit 511 | - Fix “Visual Noise Level” demo page 512 | 513 | # 0.5.2 / 2018-06-05 514 | 515 | - Improve Test Failure Messages ([#103](https://github.com/ember-a11y/ember-a11y-testing/pull/103)) 516 | - Update README with aXe Options and Scoped Auditing ([#104](<(https://github.com/ember-a11y/ember-a11y-testing/pull/104)>)) 517 | - Upgrade axe-core to 2.6.1 ([#98](<(https://github.com/ember-a11y/ember-a11y-testing/pull/98)>)) 518 | - Fix: `a11yAudit` not using default `axeOptions` ([#100](<(https://github.com/ember-a11y/ember-a11y-testing/pull/100)>)) 519 | 520 | # 0.5.1 / 2018-02-20 521 | 522 | - Update Ember-CLI to 2.18.x ([#94](<(https://github.com/ember-a11y/ember-a11y-testing/pull/94)>)) 523 | - Add check of `isDestroyed` when running an audit ([#92](<(https://github.com/ember-a11y/ember-a11y-testing/pull/92)>)) 524 | 525 | # 0.5.0 / 2017-10-19 526 | 527 | - Update a couple more dependencies 528 | - Update runtime dependencies 529 | 530 | # 0.4.4 / 2017-10-05 531 | 532 | - Package upgrades and locking version of htmlbars-inline-precompile 533 | - update README -change double-ticks to single-ticks 534 | - update README to combine axeOptions examples 535 | - README - example to disable color contrast scroll 536 | 537 | # 0.4.3 / 2017-06-21 538 | 539 | - Present information in an easy-to-digest format #18 (#78) 540 | - Add example on Readme to disable specific checks 541 | 542 | # 0.4.2 / 2017-05-19 543 | 544 | - Add release notes 545 | - Update axe-core version and range 546 | 547 | # 0.4.1 / 2017-03-24 548 | 549 | - Don't include content for production builds 550 | 551 | # 0.4.0 / 2017-03-22 552 | 553 | - Update readme with new features/info 554 | - Support passing options as a single argument to a11yAudit 555 | - feat(a11yAudit): opt in to a11yAudit 556 | - Fix axe-component error test 557 | - Ensure `super` call is bounded 558 | 559 | # 0.3.0 / 27-02-2017 560 | 561 | - Replace `jshint` with `eslint` 562 | - Update `readme` with new, simpler test API 563 | - Remove auto-run feature 564 | - Ensure `axe-core` is included in production test builds 565 | - Remove blueprint tests from `npm` scripts 566 | - Remove `babel` polyfill from test app 567 | 568 | # 0.2.2 / 20-02-2017 569 | 570 | - Allow `turnAuditOff` to be defined globally 571 | 572 | # 0.2.1 / 15-02-2017 573 | 574 | - Include axe-core in all non-prod environments 575 | 576 | # 0.2.0 / 15-02-2017 577 | 578 | - Properly report exceptions 579 | - Move `axe-core` to `npm` dependency 580 | - Add `a11y-audit` test helper 581 | - Add waiter to ensure audits finish running before tests finish 582 | - Upgrade all dependencies 583 | - Remove application references in initializers 584 | 585 | # 0.1.6 / 16-08-2016 586 | 587 | - Ensure version of `axe-core` added to projects is in sync with the current dependency. 588 | - Implement initial testing for this behavior within `node-tests/blueprint-test.js` 589 | 590 | # 0.1.5 / 09-08-2016 591 | 592 | - Fix parsing of `axeViolationClassNames`, which previously wasn't reading strings the right way ([#36](https://github.com/ember-a11y/ember-a11y-testing/pull/36)) 593 | 594 | # 0.1.4 / 11-07-2016 595 | 596 | - Update to `axe-core` @ 2.0.5. For `ember-a11y-testing`, that fixes [this](https://github.com/ember-a11y/ember-a11y-testing/issues/29) 597 | 598 | # 0.1.3 / 16-06-2016 599 | 600 | - Improve the [styles used for highlighting violations](https://github.com/ember-a11y/ember-a11y-testing/pull/28). 601 | 602 | # 0.1.2 / 16-06-2016 603 | 604 | - Create `CHANGELOG.md` 👍 605 | - Update to `axe-core` @ 1.1.1 and ensure that `addBowerPackageToProject` in the default blueprint explicitly matches the version. 606 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | - `git clone https://github.com/ember-a11y/ember-a11y-testing.git` 6 | - `cd ember-a11y-testing` 7 | - `pnpm install` 8 | 9 | ## Linting 10 | 11 | * `pnpm lint` 12 | * `pnpm lint:fix` 13 | 14 | ## Running tests 15 | 16 | - `ember test` – Runs the test suite on the current Ember version 17 | - `ember test --server` – Runs the test suite in "watch mode" 18 | - `ember try:each` – Runs the test suite against multiple Ember versions 19 | 20 | ## Running the dummy application 21 | 22 | - `ember serve` 23 | - Visit the dummy application at [http://localhost:4200](http://localhost:4200). 24 | 25 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-a11y-testing 2 | 3 | [![CI Build](https://github.com/ember-a11y/ember-a11y-testing/workflows/CI/badge.svg)](https://github.com/ember-a11y/ember-a11y-testing/actions/workflows/ci.yml?query=workflow%3ACI) 4 | [![NPM Version](https://badge.fury.io/js/ember-a11y-testing.svg)](http://badge.fury.io/js/ember-a11y-testing) 5 | [![Ember Observer Score](https://emberobserver.com/badges/ember-a11y-testing.svg)](https://emberobserver.com/addons/ember-a11y-testing) 6 | 7 | `ember-a11y-testing` is a wrapper around [Deque Labs'](https://github.com/dequelabs) 8 | [axe-core](https://github.com/dequelabs/axe-core) accessibility testing engine. 9 | It integrates into your testing environment using either a one-time setup, or in 10 | individual tests using an `a11yAudit()` test helper. 11 | 12 | ## Compatibility 13 | 14 | - Ember.js v4.0.0 or above 15 | - Ember CLI v3.8 or above 16 | - Node.js v16 or above 17 | - `@ember/test-helpers` v3.0.3 or above 18 | 19 | Note: we enforce a peerDependency of `@ember/test-helpers`. If you encounter the following message: 20 | 21 | ```shell 22 | ember-a11y-testing has the following unmet peerDependencies: 23 | * @ember/test-helpers: `^2.0.0`; it was resolved to `x.x.x` 24 | ``` 25 | 26 | please update your version of `@ember/test-helpers` in your package.json accordingly. 27 | 28 | Note: Users who are using Node <= 14 in their apps should use `ember-a11y-testing@^5.2.1`. 29 | 30 | ## Installation 31 | 32 | ```bash 33 | ember install ember-a11y-testing 34 | ``` 35 | 36 | ## Usage 37 | 38 | Usage of `ember-a11y-testing` in your tests can be done in one of two ways: 39 | 40 | 1. A one-time setup in your tests/test-helper.js file using `setupGlobalA11yHooks` 41 | 1. In individual tests using the `a11yAudit` test helper. 42 | 43 | ### axe Options 44 | 45 | When using the `a11yAudit` helper, you can pass in `axe-core` options that are passed to `axe.run`. 46 | These options are documented in the [axe-core API docs](https://www.deque.com/axe/core-documentation/api-documentation/#user-content-options-parameter). 47 | The rule definitions are documented on [dequeuniversity.com/rules](https://dequeuniversity.com/rules/axe/4.0). 48 | 49 | Each of the following sections individually details how to set aXe options for your tests. 50 | 51 | ### `setupGlobalA11yHooks` Usage 52 | 53 | The `setupGlobalA11yHooks` function is intended to be imported and invoked a single time in `tests/test-helper.js` for your entire test suite. 54 | 55 | ```ts 56 | export interface InvocationStrategy { 57 | (helperName: string, label: string): boolean; 58 | } 59 | 60 | export interface GlobalA11yHookOptions { 61 | helpers: HelperName[]; 62 | } 63 | 64 | type HelperName = 65 | | 'blur' 66 | | 'click' 67 | | 'doubleClick' 68 | | 'fillIn' 69 | | 'focus' 70 | | 'render' 71 | | 'scrollTo' 72 | | 'select' 73 | | 'tab' 74 | | 'tap' 75 | | 'triggerEvent' 76 | | 'triggerKeyEvent' 77 | | 'typeIn' 78 | | 'visit'; 79 | 80 | export const DEFAULT_A11Y_TEST_HELPER_NAMES = [ 81 | 'visit', 82 | 'click', 83 | 'doubleClick', 84 | 'tap', 85 | ]; 86 | 87 | export function setupGlobalA11yHooks( 88 | shouldAudit: InvocationStrategy, 89 | audit: (...args: any[]) => PromiseLike = a11yAudit, 90 | options: GlobalA11yHookOptions = { helpers: DEFAULT_A11Y_TEST_HELPER_NAMES } 91 | ); 92 | ``` 93 | 94 | The `setupGlobalA11yHooks` function takes three parameters: 95 | 96 | - `shouldAudit`: An `InvocationStrategy` - a [predicate function](https://stackoverflow.com/a/1344021/769) that takes a `helperName` and a `label`, and returns a `boolean` indicating whether or not to perform the audit. 97 | - `audit` (optional): The audit function, which performs the `axe-core` audit, defaulting to `a11yAudit`. This allows you to potentially wrap the `a11yAudit` test helper with custom logic. 98 | - `options` (optional): Setup options, which allow you to specify after which test helpers to run the audit. 99 | 100 | Using a custom `InvocationStrategy` implementation will allow you to maintain a high level of control over your test invocations. Examples of invocation strategies can be found in [this](https://github.com/ember-a11y/ember-a11y-testing/blob/50ef5f8fff4aa91d7a85b9feee5b1ce9bf380df9/tests/acceptance/setup-global-a11y-hooks-test.ts#L32) [repository's](https://github.com/ember-a11y/ember-a11y-testing/blob/50ef5f8fff4aa91d7a85b9feee5b1ce9bf380df9/tests/acceptance/setup-global-a11y-hooks-test.ts#L81) [tests](https://github.com/ember-a11y/ember-a11y-testing/blob/50ef5f8fff4aa91d7a85b9feee5b1ce9bf380df9/tests/acceptance/setup-global-a11y-hooks-test.ts#L135). 101 | 102 | To use, import and invoke the global setup function, passing in your specific invocation strategy: 103 | 104 | ```js 105 | // tests/test-helper.js 106 | import Application from 'my-app/app'; 107 | import config from 'my-app/config/environment'; 108 | import { setApplication } from '@ember/test-helpers'; 109 | import { start } from 'ember-qunit'; 110 | import { setupGlobalA11yHooks } from 'ember-a11y-testing/test-support'; 111 | setApplication(Application.create(config.APP)); 112 | 113 | setupGlobalA11yHooks(() => true); 114 | 115 | start(); 116 | ``` 117 | 118 | :warning: It's important to note that you must also use the [`enableA11yAudit`](#force-running-audits) query parameter in order to force audits. This setting is required in addition to any invocation strategy you provide. 119 | 120 | By default, audits will be run on `visit`, `click`, `doubleClick`, and `tap`. To add additional helpers to hook into, specify them by name in the `options.helpers` argument. Note that this option specifies the _complete_ set of helpers to hook into; to include the defaults you must import them and splat them into the array as shown below. 121 | 122 | ```js 123 | import { 124 | setupGlobalA11yHooks, 125 | DEFAULT_A11Y_TEST_HELPER_NAMES, 126 | } from 'ember-a11y-testing/test-support'; 127 | 128 | setupGlobalA11yHooks(() => true, { 129 | helpers: [...DEFAULT_A11Y_TEST_HELPER_NAMES, 'render', 'tab'], 130 | }); 131 | ``` 132 | 133 | #### Setting Options using `setRunOptions` 134 | 135 | You can provide options to axe-core for your tests using the `setRunOptions` API. This API is helpful if you don't have access to the `a11yAudit` calls directly, such as when using the `setupGlobalA11yHooks`, or if you want to set the same options for all tests in a module. 136 | 137 | Options can be set a few ways: 138 | 139 | Globally: 140 | 141 | ```javascript 142 | // tests/test-helper.js 143 | import { setRunOptions } from 'ember-a11y-testing/test-support'; 144 | 145 | setRunOptions({ 146 | rules: { 147 | region: { enabled: true }, 148 | }, 149 | }); 150 | ``` 151 | 152 | Test module level: 153 | 154 | ```javascript 155 | import { module, test } from 'qunit'; 156 | import { setRunOptions } from 'ember-a11y-testing/test-support'; 157 | 158 | module('some test module', function (hooks) { 159 | hooks.beforeEach(function () { 160 | setRunOptions({ 161 | rules: { 162 | region: { enabled: true }, 163 | }, 164 | }); 165 | }); 166 | 167 | // ... 168 | }); 169 | ``` 170 | 171 | Individual test level: 172 | 173 | ```javascript 174 | import { module, test } from 'qunit'; 175 | import { a11yAudit, setRunOptions } from 'ember-a11y-testing/test-support'; 176 | 177 | module('some test module', function (hooks) { 178 | test('some test', function (assert) { 179 | setRunOptions({ 180 | rules: { 181 | region: { enabled: true }, 182 | }, 183 | }); 184 | 185 | // ... 186 | a11yAudit(); 187 | // ... 188 | }); 189 | 190 | // ... 191 | }); 192 | ``` 193 | 194 | When using `setRunOptions` during a test, the options you set are automatically reset when the test completes. 195 | 196 | ### `a11yAudit` Usage 197 | 198 | `ember-a11y-testing` provides a test helper to run accessibility audits on specific tests within your test suite. The `a11yAudit` helper is an async test helper which can be used in a similar fashion to other `@ember/test-helpers` helpers: 199 | 200 | In Application tests: 201 | 202 | ```javascript 203 | import { visit } from '@ember/test-helpers'; 204 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 205 | 206 | module('Some module', function () { 207 | //... 208 | 209 | test('Some test case', async function (assert) { 210 | await visit('/'); 211 | await a11yAudit(); 212 | assert.ok(true, 'no a11y errors found!'); 213 | }); 214 | }); 215 | ``` 216 | 217 | The helper is also able to be used in Integration/Unit tests like so: 218 | 219 | ```javascript 220 | import { render } from '@ember/test-helpers'; 221 | import { a11yAudit } from 'ember-a11y-testing/test-support'; 222 | 223 | // ...elided for brevity 224 | 225 | test('Some test case', function (assert) { 226 | await render(hbs`{{some-component}}`); 227 | 228 | let axeOptions = { 229 | rules: { 230 | 'button-name': { 231 | enabled: false, 232 | }, 233 | }, 234 | }; 235 | 236 | await a11yAudit(this.element, axeOptions); 237 | 238 | assert.ok(true, 'no a11y errors found!'); 239 | }); 240 | ``` 241 | 242 | #### Setting Options with `a11yAudit` 243 | 244 | The helper can optionally accept a "context" on which to focus the audit as 245 | either a selector string or an HTML element. You can also provide a secondary 246 | parameter to specify axe-core options: 247 | 248 | ```js 249 | test('Some test case', async function (assert) { 250 | let axeOptions = { 251 | rules: { 252 | 'button-name': { 253 | enabled: false, 254 | }, 255 | }, 256 | }; 257 | 258 | await visit('/'); 259 | await a11yAudit(axeOptions); 260 | 261 | assert.ok(true, 'no a11y errors found!'); 262 | }); 263 | ``` 264 | 265 | Or specify options as a single argument: 266 | 267 | ```js 268 | test('Some test case', async function (assert) { 269 | let axeOptions = { 270 | rules: { 271 | 'button-name': { 272 | enabled: false, 273 | }, 274 | }, 275 | }; 276 | 277 | await visit('/'); 278 | await a11yAudit('.modal', axeOptions); 279 | 280 | assert.ok(true, 'no a11y errors found!'); 281 | }); 282 | ``` 283 | 284 | ### Force Running audits 285 | 286 | `ember-a11y-testing` allows you to force audits if `enableA11yAudit` is set as a query param 287 | on the test page or the `ENABLE_A11Y_AUDIT` environment variable is provided. This is useful if you want to conditionally run accessibility audits, such 288 | as during nightly build jobs. 289 | 290 | To do so, import and use `shouldForceAudit` from `ember-a11y-testing`, as shown below. 291 | 292 | ```javascript 293 | // `&enableA11yAudit` set in the URL 294 | import { a11yAudit, shouldForceAudit } from 'ember-a11y-testing/test-support'; 295 | 296 | test( 297 | 'Some test case', 298 | await function (assert) { 299 | await visit('/'); 300 | 301 | if (shouldForceAudit()) { 302 | await a11yAudit(); 303 | } 304 | assert.ok(true, 'no a11y errors found!'); 305 | } 306 | ); 307 | ``` 308 | 309 | ```javascript 310 | // No `enableA11yAudit` set in the URL 311 | import { a11yAudit, shouldForceAudit } from 'ember-a11y-testing/test-support'; 312 | 313 | test( 314 | 'Some test case', 315 | await function (assert) { 316 | await visit('/'); 317 | 318 | if (shouldForceAudit()) { 319 | await a11yAudit(); // will not run 320 | } 321 | // ... 322 | } 323 | ); 324 | ``` 325 | 326 | You can also create your own app-level helper, which will conditionally check whether to run the audits or not: 327 | 328 | ```javascript 329 | export function a11yAuditIf(contextSelector, axeOptions) { 330 | if (shouldForceAudit()) { 331 | return a11yAudit(contextSelector, axeOptions); 332 | } 333 | 334 | return resolve(undefined, 'a11y audit not run'); 335 | } 336 | ``` 337 | 338 | #### QUnit and Testem integration 339 | 340 | You can setup a new configuration checkbox in QUnit and Testem by using the `setupQUnitA11yAuditToggle`. 341 | When the checkbox is checked, it will set `enableA11yAudit` as a query param. 342 | 343 | To use, import and invoke the setup function, passing in your QUnit instance: 344 | 345 | ```js 346 | // tests/test-helper.js 347 | import Application from 'my-app/app'; 348 | import config from 'my-app/config/environment'; 349 | import * as QUnit from 'qunit'; 350 | import { setApplication } from '@ember/test-helpers'; 351 | import { start } from 'ember-qunit'; 352 | import { setupGlobalA11yHooks, setupQUnitA11yAuditToggle } from 'ember-a11y-testing/test-support'; 353 | setApplication(Application.create(config.APP)); 354 | 355 | setupGlobalA11yHooks(() => true); 356 | setupQUnitA11yAuditToggle(QUnit); 357 | 358 | start(); 359 | ``` 360 | 361 | ### Logging violations to the console 362 | 363 | This addon provides the capability of summarizing all violations found during tests, and outputting those failures to the console once the test suite is completed. To enable this functionality, import `setupConsoleLogger` and invoke in your `tests/test-helper.js` file: 364 | 365 | ```js 366 | import Application from 'my-app/app'; 367 | import config from 'my-app/config/environment'; 368 | import { setApplication } from '@ember/test-helpers'; 369 | import { start } from 'ember-qunit'; 370 | import { setupConsoleLogger } from 'ember-a11y-testing/test-support'; 371 | 372 | setApplication(Application.create(config.APP)); 373 | 374 | setupConsoleLogger(); 375 | 376 | start(); 377 | ``` 378 | 379 | Example: 380 | 381 | Console Logger Output Example 382 | 383 | ### Test Middleware 384 | 385 | This addon provides middleware - code that allows the browser to talk to the node process running the tests via testem. This is useful in scenarios such as internal compliance monitoring used to track accessibility grades. 386 | 387 | The middleware reporter writes the results containing all violations detected in all tests to a JSON file stored in a directory, `ember-a11y-report`, in your application or addon's root directory. 388 | 389 | :warning: **Audit report files get generated in an additive manner, typically resulting in the `a11y-audit-report` directory growing in size as subsequent test suites are run. Environments with specific space size restrictions will require an explicit strategy to manage the deletion of older reports, as this addon no longer does so.** 390 | 391 | To use the middleware reporter, import `setupMiddlewareReporter` and invoke in your `tests/test-helper.js` file: 392 | 393 | ```js 394 | import Application from 'my-app/app'; 395 | import config from 'my-app/config/environment'; 396 | import { setApplication } from '@ember/test-helpers'; 397 | import { start } from 'ember-qunit'; 398 | import { setupMiddlewareReporter } from 'ember-a11y-testing/test-support'; 399 | 400 | setApplication(Application.create(config.APP)); 401 | 402 | setupMiddlewareReporter(); 403 | 404 | start(); 405 | ``` 406 | 407 | A helper function is available to use the middleware reporter conditionally, allowing interoperability between the default reporter and the middleware reporter. Import `useMiddlewareReporter` and apply as a check around the `setupMiddlewareReporter` function in `tests/test-helper.js`. The middleware reporter will now only be invoked when `enableA11yMiddlewareReporter` is set as a query param on the test page or the `ENABLE_A11Y_MIDDLEWARE_REPORTER` environment variable is provided. 408 | 409 | ```js 410 | import Application from 'my-app/app'; 411 | import config from 'my-app/config/environment'; 412 | import { setApplication } from '@ember/test-helpers'; 413 | import { start } from 'ember-qunit'; 414 | import { 415 | setupMiddlewareReporter, 416 | useMiddlewareReporter, 417 | } from 'ember-a11y-testing/test-support'; 418 | 419 | setApplication(Application.create(config.APP)); 420 | 421 | if (useMiddlewareReporter()) { 422 | // Only runs if `enableA11yMiddlewareReporter` is set in URL 423 | setupMiddlewareReporter(); 424 | } 425 | 426 | start(); 427 | ``` 428 | 429 | Note, as a convenience, `useMiddlewareReporter` automatically forces audits, thus explicitly specifying 430 | the `enableA11yAudit` query param or the `ENABLE_A11Y_AUDIT` environment variable is unnecessary. 431 | 432 | ### Development Usage 433 | 434 | While this addon previously included a number of components that would aid in identifying axe violations during development, those have been deprecated in favor of other, industry standard tools such as: 435 | 436 | - [**Accessibility Insights for Web**](https://accessibilityinsights.io/docs/en/web/overview) - Accessibility Insights for Web helps developers find and fix accessibility issues in web apps and sites. This browser extension for Chrome and the new Microsoft Edge runs on Windows, MacOS, and Linux computers. 437 | - [**Lighthouse**](https://developers.google.com/web/tools/lighthouse) - an open-source, automated tool for improving the quality of web pages. You can run it against any web page, public or requiring authentication. It has audits for performance, accessibility, progressive web apps, SEO and more. 438 | - [**Sa11y**](https://ryersondmp.github.io/sa11y/) - an accessibility quality assurance tool that visually highlights common accessibility and usability issues. Geared towards content authors, Sa11y indicates errors or warnings at the source with a simple tooltip on how to fix. 439 | - [**axe Chrome extension**](https://www.deque.com/axe/browser-extensions/) - a free axe browser extension ideal for development teams to test web applications to help identify and resolve common accessibility issues. 440 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | Releases in this repo are mostly automated using [release-plan](https://github.com/embroider-build/release-plan/). Once you label all your PRs correctly (see below) you will have an automatically generated PR that updates your CHANGELOG.md file and a `.release-plan.json` that is used to prepare the release once the PR is merged. 4 | 5 | ## Preparation 6 | 7 | Since the majority of the actual release process is automated, the remaining tasks before releasing are: 8 | 9 | - correctly labeling **all** pull requests that have been merged since the last release 10 | - updating pull request titles so they make sense to our users 11 | 12 | Some great information on why this is important can be found at [keepachangelog.com](https://keepachangelog.com/en/1.1.0/), but the overall 13 | guiding principle here is that changelogs are for humans, not machines. 14 | 15 | When reviewing merged PR's the labels to be used are: 16 | 17 | - breaking - Used when the PR is considered a breaking change. 18 | - enhancement - Used when the PR adds a new feature or enhancement. 19 | - bug - Used when the PR fixes a bug included in a previous release. 20 | - documentation - Used when the PR adds or updates documentation. 21 | - internal - Internal changes or things that don't fit in any other category. 22 | 23 | **Note:** `release-plan` requires that **all** PRs are labeled. If a PR doesn't fit in a category it's fine to label it as `internal` 24 | 25 | ## Release 26 | 27 | Once the prep work is completed, the actual release is straight forward: you just need to merge the open [Plan Release](https://github.com/ember-a11y/ember-a11y-testing/pulls?q=is%3Apr+is%3Aopen+%22Prepare+Release%22+in%3Atitle) PR 28 | -------------------------------------------------------------------------------- /addon-test-support/audit.ts: -------------------------------------------------------------------------------- 1 | import type { RunOptions, ElementContext, ContextObject } from 'axe-core'; 2 | import { run } from 'axe-core'; 3 | import { mark, markEndAndMeasure } from './performance'; 4 | import { getRunOptions } from './run-options'; 5 | import { reportA11yAudit } from './reporter'; 6 | import { waitForPromise } from '@ember/test-waiters'; 7 | 8 | type MaybeElementContext = ElementContext | RunOptions | undefined; 9 | 10 | /** 11 | * Validation function used to determine if we have the shape of an {ElementContext} object. 12 | * 13 | * Function mirrors what axe-core uses for internal param validation. 14 | * https://github.com/dequelabs/axe-core/blob/d5b6931cba857a5c787d912ee56bdd973e3742d4/lib/core/public/run.js#L4 15 | * 16 | * @param potential 17 | */ 18 | export function _isContext(potential: MaybeElementContext) { 19 | 'use strict'; 20 | switch (true) { 21 | case typeof potential === 'string': 22 | case Array.isArray(potential): 23 | case self.Node && potential instanceof self.Node: 24 | case self.NodeList && potential instanceof self.NodeList: 25 | return true; 26 | 27 | case typeof potential !== 'object': 28 | return false; 29 | 30 | case (potential).include !== undefined: 31 | case (potential).exclude !== undefined: 32 | return true; 33 | 34 | default: 35 | return false; 36 | } 37 | } 38 | 39 | /** 40 | * Normalize the optional params of axe.run() 41 | * 42 | * Influenced by https://github.com/dequelabs/axe-core/blob/d5b6931cba857a5c787d912ee56bdd973e3742d4/lib/core/public/run.js#L35 43 | * 44 | * @param elementContext 45 | * @param runOptions 46 | */ 47 | export function _normalizeRunParams( 48 | elementContext?: MaybeElementContext, 49 | runOptions?: RunOptions | undefined, 50 | ): [ElementContext, RunOptions] { 51 | let context: ElementContext; 52 | let options: RunOptions | undefined; 53 | 54 | if (!_isContext(elementContext)) { 55 | options = elementContext; 56 | context = '#ember-testing-container'; 57 | } else { 58 | context = elementContext; 59 | options = runOptions; 60 | } 61 | 62 | if (typeof options !== 'object') { 63 | options = getRunOptions() || {}; 64 | } 65 | 66 | return [context, options]; 67 | } 68 | 69 | /** 70 | * Runs the axe a11y audit with the given context selector and options. 71 | * 72 | * @function a11yAudit 73 | * @public 74 | * @param contextSelector A DOM node specifying the context to run the audit in. Defaults to '#ember-testing-container' if not specified. 75 | * @param axeOptions options to provide to the axe audit. Defaults axe-core defaults. 76 | */ 77 | export default function a11yAudit( 78 | contextSelector: MaybeElementContext = '#ember-testing-container', 79 | axeOptions?: RunOptions | undefined, 80 | ): PromiseLike { 81 | mark('a11y_audit_start'); 82 | 83 | let [context, options] = _normalizeRunParams(contextSelector, axeOptions); 84 | 85 | document.body.classList.add('axe-running'); 86 | 87 | return waitForPromise(run(context, options)) 88 | .then(reportA11yAudit) 89 | .finally(() => { 90 | document.body.classList.remove('axe-running'); 91 | markEndAndMeasure('a11y_audit', 'a11y_audit_start', 'a11y_audit_end'); 92 | }); 93 | } 94 | -------------------------------------------------------------------------------- /addon-test-support/cli-options.ts: -------------------------------------------------------------------------------- 1 | export const ENABLE_A11Y_MIDDLEWARE_REPORTER = false; 2 | export const ENABLE_A11Y_AUDIT = false; 3 | -------------------------------------------------------------------------------- /addon-test-support/format-violation.ts: -------------------------------------------------------------------------------- 1 | import type { Result } from 'axe-core'; 2 | 3 | /** 4 | * Formats the axe violation for human consumption 5 | * 6 | * @param {Partial} violation 7 | * @param {string[]} markup (optional) string of HTML relevant to the violation 8 | */ 9 | export default function formatViolation( 10 | violation: Partial, 11 | markup: string[], 12 | ) { 13 | if (!violation.impact || !violation.help || !violation.helpUrl) { 14 | throw new Error( 15 | 'formatViolation called with improper structure of parameter: violation. Required properties: impact, help, helpUrl.', 16 | ); 17 | } 18 | 19 | let count = 1; 20 | let formattedMarkup = ''; 21 | 22 | if (markup.length) { 23 | count = markup.length; 24 | formattedMarkup = ` Offending nodes are: \n${markup.join('\n')}`; 25 | } 26 | 27 | let plural = count === 1 ? '' : 's'; 28 | let violationCount = `Violated ${count} time${plural}.`; 29 | 30 | return `[${violation.impact}]: ${violation.help} \n${violationCount}${formattedMarkup}\n${violation.helpUrl}`; 31 | } 32 | -------------------------------------------------------------------------------- /addon-test-support/index.ts: -------------------------------------------------------------------------------- 1 | export { default as a11yAudit } from './audit'; 2 | export { setRunOptions, getRunOptions } from './run-options'; 3 | export { setEnableA11yAudit, shouldForceAudit } from './should-force-audit'; 4 | export { useMiddlewareReporter } from './use-middleware-reporter'; 5 | export { 6 | setupGlobalA11yHooks, 7 | teardownGlobalA11yHooks, 8 | DEFAULT_A11Y_TEST_HELPER_NAMES, 9 | } from './setup-global-a11y-hooks'; 10 | export { 11 | setCustomReporter, 12 | DEFAULT_REPORTER as _DEFAULT_REPORTER, 13 | } from './reporter'; 14 | export { 15 | TEST_SUITE_RESULTS as _TEST_SUITE_RESULTS, 16 | middlewareReporter as _middlewareReporter, 17 | pushTestResult as _pushTestResult, 18 | setupMiddlewareReporter, 19 | } from './setup-middleware-reporter'; 20 | export { storeResults, printResults } from './logger'; 21 | export { setupConsoleLogger } from './setup-console-logger'; 22 | export { setupQUnitA11yAuditToggle } from './setup-qunit'; 23 | 24 | export type { InvocationStrategy, A11yAuditReporter } from './types'; 25 | -------------------------------------------------------------------------------- /addon-test-support/logger.ts: -------------------------------------------------------------------------------- 1 | import axeCore from 'axe-core'; 2 | import type { AxeResults, NodeResult, RelatedNode, Result } from 'axe-core'; 3 | 4 | /** 5 | * This file is heavily borrowed from https://github.com/dequelabs/react-axe/blob/d3245b32fc5ed19e3c7b2c43d2815fe63f5875cb/index.ts 6 | * 7 | * An issue (https://github.com/dequelabs/react-axe/issues/180) has been opened which 8 | * suggests extracting out the common pieces of axe formatting to a separate repository, 9 | * which would allow a lot of these functions to be removed. 10 | */ 11 | 12 | // @reference axe-core https://github.com/dequelabs/axe-core/blob/develop/lib/core/base/audit.js 13 | type AxeCoreNodeResultKey = 'any' | 'all' | 'none'; 14 | 15 | interface AxeWithAudit { 16 | _audit: { 17 | data: { 18 | failureSummaries: { 19 | any: { 20 | failureMessage: (args: string[]) => string; 21 | }; 22 | all: { 23 | failureMessage: (args: string[]) => string; 24 | }; 25 | none: { 26 | failureMessage: (args: string[]) => string; 27 | }; 28 | }; 29 | }; 30 | }; 31 | } 32 | 33 | // contrasted against Chrome default color of #ffffff 34 | const lightTheme = { 35 | serious: '#d93251', 36 | minor: '#d24700', 37 | text: 'black', 38 | }; 39 | 40 | // contrasted against Safari dark mode color of #535353 41 | const darkTheme = { 42 | serious: '#ffb3b3', 43 | minor: '#ffd500', 44 | text: 'white', 45 | }; 46 | 47 | const theme = 48 | window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches 49 | ? darkTheme 50 | : lightTheme; 51 | 52 | const boldCourier = 'font-weight:bold;font-family:Courier;'; 53 | const critical = `color:${theme.serious};font-weight:bold;`; 54 | const serious = `color:${theme.serious};font-weight:normal;`; 55 | const moderate = `color:${theme.minor};font-weight:bold;`; 56 | const minor = `color:${theme.minor};font-weight:normal;`; 57 | const defaultReset = `font-color:${theme.text};font-weight:normal;`; 58 | 59 | const testSuiteResults: Result[][] = []; 60 | const cache: { [key: string]: string } = {}; 61 | 62 | function deduplicateViolations(results: AxeResults) { 63 | return results.violations.filter((result) => { 64 | result.nodes = result.nodes.filter((node) => { 65 | const key: string = node.target.toString() + result.id; 66 | const retVal = !cache[key]; 67 | cache[key] = key; 68 | return retVal; 69 | }); 70 | return !!result.nodes.length; 71 | }); 72 | } 73 | 74 | /** 75 | * Log the axe result node to the console 76 | * 77 | * @param {NodeResult} node 78 | * @param {Function} logFn console log function to use (error, warn, log, etc.) 79 | */ 80 | function logElement( 81 | node: NodeResult | RelatedNode, 82 | logFn: (...args: any[]) => void, 83 | ): void { 84 | const el = document.querySelector(node.target.toString()); 85 | if (!el) { 86 | logFn('Selector: %c%s', boldCourier, node.target.toString()); 87 | } else { 88 | logFn('Element: %o', el); 89 | } 90 | } 91 | 92 | /** 93 | * Log the axe result node html to the console 94 | * 95 | * @param {NodeResult} node 96 | */ 97 | function logHtml(node: NodeResult | RelatedNode): void { 98 | console.log('HTML: %c%s', boldCourier, node.html); 99 | } 100 | 101 | /** 102 | * Log the failure message of a node result. 103 | * 104 | * @param {NodeResult} node 105 | * @param {String} key which check array to log from (any, all, none) 106 | */ 107 | function logFailureMessage(node: NodeResult, key: AxeCoreNodeResultKey): void { 108 | // this exists on axe but we don't export it as part of the typescript 109 | // namespace, so just let me use it as I need 110 | const message: string = ( 111 | axeCore as unknown as AxeWithAudit 112 | )._audit.data.failureSummaries[key].failureMessage( 113 | node[key].map((check) => check.message || ''), 114 | ); 115 | 116 | console.error(message); 117 | } 118 | 119 | /** 120 | * Log as a group the node result and failure message. 121 | * 122 | * @param {NodeResult} node 123 | * @param {String} key which check array to log from (any, all, none) 124 | */ 125 | function failureSummary(node: NodeResult, key: AxeCoreNodeResultKey): void { 126 | if (node[key].length > 0) { 127 | logElement(node, console.groupCollapsed); 128 | logHtml(node); 129 | logFailureMessage(node, key); 130 | 131 | let relatedNodes: RelatedNode[] = []; 132 | node[key].forEach((check) => { 133 | relatedNodes = relatedNodes.concat(check.relatedNodes || []); 134 | }); 135 | 136 | if (relatedNodes.length > 0) { 137 | console.groupCollapsed('Related nodes'); 138 | relatedNodes.forEach((relatedNode) => { 139 | logElement(relatedNode, console.log); 140 | logHtml(relatedNode); 141 | }); 142 | console.groupEnd(); 143 | } 144 | 145 | console.groupEnd(); 146 | } 147 | } 148 | 149 | /** 150 | * @public 151 | * @param results The axe results. 152 | */ 153 | export function storeResults(results: AxeResults) { 154 | if (results.violations.length > 0) { 155 | testSuiteResults.push(deduplicateViolations(results)); 156 | } 157 | } 158 | 159 | /** 160 | * Prints aggregated axe results to the console. 161 | * 162 | * @public 163 | */ 164 | export function printResults() { 165 | if (testSuiteResults.length) { 166 | console.group('%cAxe issues', serious); 167 | testSuiteResults.forEach((results) => { 168 | results.forEach((result) => { 169 | let fmt: string; 170 | switch (result.impact) { 171 | case 'critical': 172 | fmt = critical; 173 | break; 174 | case 'serious': 175 | fmt = serious; 176 | break; 177 | case 'moderate': 178 | fmt = moderate; 179 | break; 180 | case 'minor': 181 | fmt = minor; 182 | break; 183 | default: 184 | fmt = minor; 185 | break; 186 | } 187 | console.groupCollapsed( 188 | '%c%s: %c%s %s', 189 | fmt, 190 | result.impact, 191 | defaultReset, 192 | result.help, 193 | result.helpUrl, 194 | ); 195 | result.nodes.forEach((node) => { 196 | failureSummary(node, 'any'); 197 | failureSummary(node, 'none'); 198 | }); 199 | console.groupEnd(); 200 | }); 201 | }); 202 | console.groupEnd(); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /addon-test-support/performance.ts: -------------------------------------------------------------------------------- 1 | const HAS_PERFORMANCE = 2 | window && 3 | typeof window.performance !== 'undefined' && 4 | typeof window.performance.mark === 'function' && 5 | typeof window.performance.measure === 'function'; 6 | 7 | /** 8 | * Utility to add a performance marker. 9 | * 10 | * @param markName 11 | */ 12 | export function mark(markName: string) { 13 | if (HAS_PERFORMANCE) { 14 | performance.mark(markName); 15 | } 16 | } 17 | 18 | /** 19 | * Utility to measure performance between the start and end markers. 20 | * 21 | * @param comment 22 | * @param startMark 23 | * @param endMark 24 | */ 25 | export function measure(comment: string, startMark: string, endMark: string) { 26 | // `performance.measure` may fail if the mark could not be found. 27 | // reasons a specific mark could not be found include outside code invoking `performance.clearMarks()` 28 | try { 29 | if (HAS_PERFORMANCE) { 30 | performance.measure(comment, startMark, endMark); 31 | } 32 | } catch (e: unknown) { 33 | // eslint-disable-next-line no-console 34 | console.warn( 35 | 'performance.measure could not be executed because of ', 36 | // @ts-ignore 37 | e.message, 38 | ); 39 | } 40 | } 41 | 42 | /** 43 | * Utility to place end marker and measure performance. 44 | * 45 | * @param comment 46 | * @param startMark 47 | * @param endMark 48 | */ 49 | export function markEndAndMeasure( 50 | comment: string, 51 | startMark: string, 52 | endMark: string, 53 | ) { 54 | if (HAS_PERFORMANCE) { 55 | mark(endMark); 56 | measure(comment, startMark, endMark); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /addon-test-support/reporter.ts: -------------------------------------------------------------------------------- 1 | import QUnit from 'qunit'; 2 | import { AxeResults } from 'axe-core'; 3 | import formatViolation from './format-violation'; 4 | import type { A11yAuditReporter } from './types'; 5 | import { storeResults } from './logger'; 6 | 7 | export const DEFAULT_REPORTER = async (results: AxeResults) => { 8 | let violations = results.violations; 9 | 10 | storeResults(results); 11 | 12 | if (violations.length) { 13 | let allViolations = violations.map((violation) => { 14 | let violationNodes = violation.nodes.map((node) => node.html); 15 | 16 | return formatViolation(violation, violationNodes); 17 | }); 18 | 19 | let allViolationMessages = allViolations.join('\n'); 20 | throw new Error( 21 | `The page should have no accessibility violations. Violations:\n${allViolationMessages} 22 | To rerun this specific failure, use the following query params: &testId=${QUnit.config.current.testId}&enableA11yAudit=true`, 23 | ); 24 | } 25 | }; 26 | 27 | /** 28 | * Reports the results of the a11yAudit. Set a custom reporter using `setCustomReporter`. 29 | */ 30 | export let reportA11yAudit: A11yAuditReporter = DEFAULT_REPORTER; 31 | 32 | /** 33 | * Sets a custom reporter, allowing implementers more specific control over how the results of the 34 | * `a11yAudit` calls are processed. Calling this function with no parameters will reset the reporter 35 | * to the default reporter. 36 | * 37 | * @param customReporter {A11yAuditReporter} The reporter to use in a11yAudit 38 | */ 39 | export function setCustomReporter( 40 | customReporter: A11yAuditReporter = DEFAULT_REPORTER, 41 | ) { 42 | reportA11yAudit = customReporter; 43 | } 44 | -------------------------------------------------------------------------------- /addon-test-support/run-options.ts: -------------------------------------------------------------------------------- 1 | import type { RunOptions } from 'axe-core'; 2 | import { getContext } from '@ember/test-helpers'; 3 | import { registerDestructor } from '@ember/destroyable'; 4 | 5 | let optionsStack: RunOptions[] = []; 6 | 7 | /** 8 | * Sets run options specific to a test. 9 | * 10 | * @param options Axe {RunOptions} to be provided to the audit helper. 11 | */ 12 | export function setRunOptions(options: RunOptions = {}) { 13 | optionsStack.push(options); 14 | 15 | let context = getContext(); 16 | if (context) { 17 | registerDestructor((context as any).owner, () => optionsStack.pop()); 18 | } 19 | } 20 | 21 | /** 22 | * Gets run options specific to a test. 23 | * 24 | * @param context Test context object, accessed using `@ember/test-helpers` `getContext` function. 25 | */ 26 | export function getRunOptions() { 27 | return optionsStack[optionsStack.length - 1]; 28 | } 29 | -------------------------------------------------------------------------------- /addon-test-support/setup-console-logger.ts: -------------------------------------------------------------------------------- 1 | import QUnit from 'qunit'; 2 | import { printResults } from './logger'; 3 | 4 | /** 5 | * Sets up the console logger to print axe results to the console when the test suite is done. 6 | */ 7 | export function setupConsoleLogger() { 8 | QUnit.done(function () { 9 | printResults(); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /addon-test-support/setup-global-a11y-hooks.ts: -------------------------------------------------------------------------------- 1 | import { registerHook, type HookUnregister } from '@ember/test-helpers'; 2 | import type { InvocationStrategy, AuditFunction } from './types'; 3 | import { getRunOptions } from './run-options'; 4 | import a11yAudit from './audit'; 5 | import { shouldForceAudit } from './should-force-audit'; 6 | 7 | export interface GlobalA11yHookOptions { 8 | helpers: HelperName[]; 9 | } 10 | 11 | type HelperName = 12 | | 'blur' 13 | | 'click' 14 | | 'doubleClick' 15 | | 'fillIn' 16 | | 'focus' 17 | | 'render' 18 | | 'scrollTo' 19 | | 'select' 20 | | 'tab' 21 | | 'tap' 22 | | 'triggerEvent' 23 | | 'triggerKeyEvent' 24 | | 'typeIn' 25 | | 'visit'; 26 | 27 | let _unregisterHooks: HookUnregister[] = []; 28 | 29 | export const DEFAULT_A11Y_TEST_HELPER_NAMES: HelperName[] = [ 30 | 'visit', 31 | 'click', 32 | 'doubleClick', 33 | 'tap', 34 | ]; 35 | 36 | /** 37 | * Sets up a11yAudit calls using `@ember/test-helpers`' `registerHook` API. 38 | * 39 | * @param shouldAudit Invocation strategy function that determines whether to run the audit helper or not. 40 | * @param audit Optional audit function used to run the audit. Allows for providing either a11yAudit 41 | * or custom audit implementation. 42 | */ 43 | export function setupGlobalA11yHooks(shouldAudit: InvocationStrategy): void; 44 | export function setupGlobalA11yHooks( 45 | shouldAudit: InvocationStrategy, 46 | audit: AuditFunction, 47 | ): void; 48 | export function setupGlobalA11yHooks( 49 | shouldAudit: InvocationStrategy, 50 | options: GlobalA11yHookOptions, 51 | ): void; 52 | export function setupGlobalA11yHooks( 53 | shouldAudit: InvocationStrategy, 54 | audit: AuditFunction, 55 | options: GlobalA11yHookOptions, 56 | ): void; 57 | export function setupGlobalA11yHooks( 58 | shouldAudit: InvocationStrategy, 59 | auditOrOptions?: AuditFunction | GlobalA11yHookOptions, 60 | options?: GlobalA11yHookOptions, 61 | ): void { 62 | let audit: AuditFunction = a11yAudit; 63 | 64 | if (typeof auditOrOptions === 'function') { 65 | audit = auditOrOptions; 66 | } else { 67 | options = auditOrOptions; 68 | } 69 | 70 | let helpers = options?.helpers ?? DEFAULT_A11Y_TEST_HELPER_NAMES; 71 | 72 | helpers.forEach((helperName) => { 73 | let hook = registerHook(helperName, 'end', async () => { 74 | if (shouldForceAudit() && shouldAudit(helperName, 'end')) { 75 | await audit(getRunOptions()); 76 | } 77 | }); 78 | 79 | _unregisterHooks.push(hook); 80 | }); 81 | } 82 | 83 | /** 84 | * Function to teardown the configured hooks. Used specifically in testing. 85 | */ 86 | export function teardownGlobalA11yHooks() { 87 | while (_unregisterHooks.length) { 88 | let hook = _unregisterHooks.shift()!; 89 | hook.unregister(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /addon-test-support/setup-middleware-reporter.ts: -------------------------------------------------------------------------------- 1 | import QUnit from 'qunit'; 2 | import { 3 | currentRouteName, 4 | currentURL, 5 | getContext, 6 | getTestMetadata, 7 | } from '@ember/test-helpers'; 8 | import type { AxeResults, Result } from 'axe-core'; 9 | import { setCustomReporter } from './reporter'; 10 | import { DEBUG } from '@glimmer/env'; 11 | import { setEnableA11yAudit } from './should-force-audit'; 12 | 13 | export interface TestMetadata { 14 | testName?: string; 15 | setupTypes: string[]; 16 | usedHelpers: string[]; 17 | [key: string]: any; 18 | 19 | readonly isRendering: boolean; 20 | readonly isApplication: boolean; 21 | } 22 | 23 | interface AxeTestResult { 24 | moduleName: string; 25 | testName: string; 26 | testMetaData: TestMetadata; 27 | urls: string[] | Set; 28 | routes: string[] | Set; 29 | helpers: string[]; 30 | stack: string; 31 | violations: Result[]; 32 | } 33 | 34 | export const TEST_SUITE_RESULTS: AxeTestResult[] = []; 35 | 36 | let currentTestResult: AxeTestResult | undefined = undefined; 37 | let currentViolationsMap: Map | undefined = undefined; 38 | let currentUrls: Set | undefined; 39 | let currentRouteNames: Set | undefined; 40 | 41 | /** 42 | * Utility to retrieve the route name corresponding to the current test. Absorbs the emitted 43 | * assertion error if route name is `null`, resulting in an empty string return value. 44 | * 45 | * @param getFn Function to use to derive the route name. 46 | * @returns Route name or empty string. 47 | */ 48 | export function _getCurrentRouteName(getFn = currentRouteName): string { 49 | let routeName = ''; 50 | 51 | try { 52 | routeName = getFn(); 53 | } catch (error: unknown) { 54 | if ( 55 | error instanceof Error && 56 | !/currentRouteName (\w|\s)+ string/.test(error.message) 57 | ) { 58 | throw error; 59 | } 60 | } 61 | 62 | return routeName; 63 | } 64 | 65 | /** 66 | * A custom reporter that is invoked once per failed a11yAudit call. This can be called 67 | * multiple times per test, and the results are accumulated until testDone. 68 | * 69 | * @param axeResults The axe results for each a11yAudit. 70 | * @returns Early returns if no violations are found. 71 | */ 72 | export async function middlewareReporter(axeResults: AxeResults) { 73 | if (axeResults.violations.length === 0) { 74 | return; 75 | } 76 | 77 | const context = getContext(); 78 | 79 | if (!currentTestResult) { 80 | let { module, testName } = QUnit.config.current; 81 | if (!context) { 82 | throw new Error( 83 | 'You tried to run ember-a11y-testing without calling one of the `setupTest` helpers from `@ember/test-helpers`. Please make sure your test setup calls `setupTest()`, `setupRenderingTest()`, or `setupApplicationTest()`!', 84 | ); 85 | } 86 | let testMetaData = getTestMetadata(context); 87 | 88 | let stack = (!DEBUG && new Error().stack) || ''; 89 | 90 | currentTestResult = { 91 | moduleName: module.name, 92 | testName, 93 | testMetaData, 94 | urls: [], 95 | routes: [], 96 | helpers: [], 97 | stack, 98 | violations: [], 99 | }; 100 | 101 | currentViolationsMap = new Map(); 102 | currentUrls = new Set(); 103 | currentRouteNames = new Set(); 104 | } 105 | 106 | if (context) { 107 | currentUrls!.add(currentURL()); 108 | currentRouteNames!.add(_getCurrentRouteName()); 109 | } 110 | 111 | axeResults.violations.forEach((violation) => { 112 | let rule = currentViolationsMap!.get(violation.id); 113 | 114 | if (rule === undefined) { 115 | currentViolationsMap!.set(violation.id, violation); 116 | } else { 117 | rule.nodes.push(...violation.nodes); 118 | } 119 | }); 120 | } 121 | 122 | /** 123 | * Invoked once per test. Accumulates the results into a set of results used for 124 | * reporting via the middleware reporter. 125 | */ 126 | export function pushTestResult() { 127 | if (typeof currentTestResult !== 'undefined') { 128 | currentTestResult.violations = [...currentViolationsMap!.values()]; 129 | currentTestResult.urls = [...currentUrls!.values()]; 130 | currentTestResult.routes = [...currentRouteNames!.values()]; 131 | currentTestResult.helpers = currentTestResult.testMetaData.usedHelpers; 132 | 133 | TEST_SUITE_RESULTS.push(currentTestResult); 134 | 135 | currentTestResult = undefined; 136 | currentViolationsMap = undefined; 137 | currentUrls = undefined; 138 | currentRouteNames = undefined; 139 | } 140 | } 141 | 142 | type TestemCallback = (config: any, data: any, callback: () => void) => void; 143 | 144 | declare global { 145 | interface Window { 146 | Testem?: { 147 | afterTests: (callback: TestemCallback) => void; 148 | }; 149 | } 150 | } 151 | 152 | /** 153 | * Sets up the middleware reporter, which reports results when the test suite is done. 154 | */ 155 | export function setupMiddlewareReporter() { 156 | setCustomReporter(middlewareReporter); 157 | 158 | setEnableA11yAudit(true); 159 | 160 | QUnit.testDone(pushTestResult); 161 | 162 | if (window.Testem) { 163 | window.Testem.afterTests(async function (_config, _data, callback) { 164 | try { 165 | await sendViolationsToServer(); 166 | } finally { 167 | callback(); 168 | } 169 | }); 170 | } else { 171 | QUnit.done(async function () { 172 | await sendViolationsToServer(); 173 | }); 174 | } 175 | } 176 | 177 | async function sendViolationsToServer() { 178 | let response = await fetch('/report-violations', { 179 | method: 'POST', 180 | headers: { 181 | 'Content-Type': 'application/json', 182 | }, 183 | body: JSON.stringify(TEST_SUITE_RESULTS), 184 | }); 185 | return response.json(); 186 | } 187 | -------------------------------------------------------------------------------- /addon-test-support/setup-qunit.ts: -------------------------------------------------------------------------------- 1 | import * as QUnit from 'qunit'; 2 | 3 | export function setupQUnitA11yAuditToggle(qunit: QUnit) { 4 | qunit.config.urlConfig.push({ 5 | id: 'enableA11yAudit', 6 | label: 'A11y audit', 7 | tooltip: 'Enable accessibility audit.', 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /addon-test-support/should-force-audit.ts: -------------------------------------------------------------------------------- 1 | import { ENABLE_A11Y_AUDIT } from './cli-options'; 2 | 3 | export function _calculateUpdatedHref( 4 | href: string, 5 | baseURI: string, 6 | enabled: boolean = false, 7 | ): string { 8 | const url = new URL(href, baseURI); 9 | const initialHref = url.href; 10 | 11 | // Set up the `enableA11yAudit` query param 12 | if (enabled) { 13 | url.searchParams.set('enableA11yAudit', ''); 14 | } else { 15 | url.searchParams.delete('enableA11yAudit'); 16 | } 17 | 18 | // Match all key-only params with '=' 19 | return url.href.replace(/([^?&]+)=(?=&|$)/g, (match, sub) => { 20 | // Only normalize `enableA11yAudit` or params that didn't initially include '=' 21 | return sub === 'enableA11yAudit' || !initialHref.includes(match) 22 | ? sub 23 | : match; 24 | }); 25 | } 26 | 27 | export function setEnableA11yAudit(enabled: boolean = false) { 28 | const href = _calculateUpdatedHref( 29 | window.location.href, 30 | document.baseURI, 31 | enabled, 32 | ); 33 | 34 | // Update the URL without reloading 35 | window.history.replaceState(null, '', href); 36 | } 37 | 38 | /** 39 | * Forces running audits. This functionality is enabled by 40 | * the presence of an `enableA11yAudit` query parameter passed to the test suite 41 | * or the `ENABLE_A11Y_AUDIT` environment variable. 42 | * 43 | * If used with `setupGlobalA11yHooks` and the query param enabled, this will override 44 | * any `InvocationStrategy` passed to that function and force the audit. 45 | */ 46 | export function shouldForceAudit() { 47 | const url = new URL(window.location.href, document.baseURI); 48 | 49 | return ENABLE_A11Y_AUDIT || url.searchParams.get('enableA11yAudit') !== null; 50 | } 51 | -------------------------------------------------------------------------------- /addon-test-support/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { AxeResults } from 'axe-core'; 2 | 3 | export interface InvocationStrategy { 4 | (helperName: string, label: string): boolean; 5 | } 6 | 7 | export interface A11yAuditReporter { 8 | (axeResults: AxeResults): Promise; 9 | } 10 | 11 | export interface AuditFunction { 12 | (...args: any[]): PromiseLike; 13 | } 14 | -------------------------------------------------------------------------------- /addon-test-support/use-middleware-reporter.ts: -------------------------------------------------------------------------------- 1 | import { ENABLE_A11Y_MIDDLEWARE_REPORTER } from './cli-options'; 2 | 3 | /** 4 | * Utility to determine whether to use the middleware reporter. This functionality is 5 | * enabled by the presence of the `enableA11yMiddlewareReporter` query parameter passed 6 | * to the test suite or the `ENABLE_A11Y_MIDDLEWARE_REPORTER` environmental variable. 7 | */ 8 | export function useMiddlewareReporter() { 9 | const url = new URL(window.location.href, document.baseURI); 10 | 11 | return ( 12 | ENABLE_A11Y_MIDDLEWARE_REPORTER || 13 | url.searchParams.get('enableA11yMiddlewareReporter') !== null 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/addon/.gitkeep -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/app/.gitkeep -------------------------------------------------------------------------------- /cli-options-filter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Filter = require('broccoli-persistent-filter'); 4 | 5 | const enableMiddlewareReporter = 6 | process.env.ENABLE_A11Y_MIDDLEWARE_REPORTER === 'true'; 7 | const enableA11yAudit = process.env.ENABLE_A11Y_AUDIT === 'true'; 8 | 9 | const replacementToken = '$1 true'; 10 | 11 | class CliOptionsFilter extends Filter { 12 | constructor() { 13 | super(...arguments); 14 | } 15 | 16 | /** 17 | * If `ENABLE_A11Y_MIDDLEWARE_REPORTER=true` or `ENABLE_A11Y_AUDIT=true` environmental 18 | * variables are specified, overwrite the corresponding values in `test-support/cli-options` 19 | * at build-time to make them accessible in the browser environment. 20 | * @override 21 | */ 22 | processString(contents) { 23 | if (enableMiddlewareReporter) { 24 | contents = contents.replace( 25 | /(ENABLE_A11Y_MIDDLEWARE_REPORTER = )false/, 26 | replacementToken, 27 | ); 28 | } 29 | 30 | if (enableA11yAudit) { 31 | contents = contents.replace( 32 | /(ENABLE_A11Y_AUDIT = )false/, 33 | replacementToken, 34 | ); 35 | } 36 | 37 | return contents; 38 | } 39 | } 40 | 41 | module.exports = CliOptionsFilter; 42 | -------------------------------------------------------------------------------- /config/ember-try-typescript.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | usePnpm: true, 3 | command: 'tsc --noEmit', 4 | scenarios: [ 5 | { 6 | name: 'typescript-4.2', 7 | npm: { 8 | typescript: '~4.2', 9 | }, 10 | }, 11 | { 12 | name: 'typescript-4.3', 13 | npm: { 14 | typescript: '~4.3', 15 | }, 16 | }, 17 | { 18 | name: 'typescript-4.4', 19 | npm: { 20 | typescript: '~4.4', 21 | }, 22 | }, 23 | { 24 | name: 'typescript-4.5', 25 | npm: { 26 | typescript: '~4.5', 27 | }, 28 | }, 29 | { 30 | name: 'typescript-next', 31 | allowedToFail: true, 32 | npm: { 33 | devDependencies: { 34 | typescript: 'next', 35 | }, 36 | }, 37 | }, 38 | ], 39 | }; 40 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup'); 5 | 6 | module.exports = async function () { 7 | return { 8 | usePnpm: true, 9 | scenarios: [ 10 | { 11 | name: 'ember-lts-4.4', 12 | npm: { 13 | devDependencies: { 14 | 'ember-source': '~4.4.0', 15 | }, 16 | }, 17 | }, 18 | { 19 | name: 'ember-lts-4.8', 20 | npm: { 21 | devDependencies: { 22 | 'ember-source': '~4.8.0', 23 | }, 24 | }, 25 | }, 26 | { 27 | name: 'ember-lts-4.12', 28 | npm: { 29 | devDependencies: { 30 | 'ember-source': '~4.12.0', 31 | }, 32 | }, 33 | }, 34 | { 35 | name: 'ember-release', 36 | npm: { 37 | devDependencies: { 38 | 'ember-source': await getChannelURL('release'), 39 | }, 40 | }, 41 | }, 42 | { 43 | name: 'ember-beta', 44 | npm: { 45 | devDependencies: { 46 | 'ember-source': await getChannelURL('beta'), 47 | }, 48 | }, 49 | }, 50 | { 51 | name: 'ember-canary', 52 | npm: { 53 | devDependencies: { 54 | 'ember-source': await getChannelURL('canary'), 55 | }, 56 | }, 57 | }, 58 | embroiderSafe(), 59 | embroiderOptimized(), 60 | ], 61 | }; 62 | }; 63 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (/* environment, appConfig */) { 4 | return {}; 5 | }; 6 | -------------------------------------------------------------------------------- /content-for/test-head-footer.html: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /docs/assets/ember-a11y-testing-console-reporter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/docs/assets/ember-a11y-testing-console-reporter.png -------------------------------------------------------------------------------- /docs/assets/violation-console-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/docs/assets/violation-console-output.png -------------------------------------------------------------------------------- /docs/assets/violation-level-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/docs/assets/violation-level-1.png -------------------------------------------------------------------------------- /docs/assets/violation-level-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/docs/assets/violation-level-2.png -------------------------------------------------------------------------------- /docs/assets/violation-level-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/docs/assets/violation-level-3.png -------------------------------------------------------------------------------- /docs/assets/violation-log-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/docs/assets/violation-log-output.png -------------------------------------------------------------------------------- /docs/assets/violation-replaced-bg-element.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/docs/assets/violation-replaced-bg-element.png -------------------------------------------------------------------------------- /docs/assets/violation-styling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/docs/assets/violation-styling.png -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function (defaults) { 6 | const self = defaults.project.findAddonByName('ember-a11y-testing'); 7 | const autoImport = self.options.autoImport; 8 | 9 | let app = new EmberAddon(defaults, { 10 | autoImport, 11 | }); 12 | 13 | /* 14 | This build file specifies the options for the dummy test app of this 15 | addon, located in `/tests/dummy` 16 | This build file does *not* influence how the addon or the app using it 17 | behave. You most likely want to be modifying `./index.js` or app's build file 18 | */ 19 | 20 | const { maybeEmbroider } = require('@embroider/test-setup'); 21 | return maybeEmbroider(app, { 22 | skipBabel: [ 23 | { 24 | package: 'qunit', 25 | }, 26 | ], 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const VersionChecker = require('ember-cli-version-checker'); 6 | const validatePeerDependencies = require('validate-peer-dependencies'); 7 | const { 8 | setupMiddlewareHooks, 9 | } = require('@scalvert/ember-setup-middleware-reporter'); 10 | const CliOptionsFilter = require('./cli-options-filter'); 11 | 12 | // The different types/area for which we have content for. 13 | const ALLOWED_CONTENT_FOR = ['test-head-footer']; 14 | 15 | module.exports = { 16 | name: require('./package').name, 17 | 18 | init() { 19 | this._super.init.apply(this, arguments); 20 | 21 | let versionChecker = new VersionChecker(this.project); 22 | 23 | validatePeerDependencies(__dirname, { 24 | resolvePeerDependenciesFrom: this.parent.root, 25 | }); 26 | 27 | const hasMagicallyProvidedQUnit = versionChecker 28 | .for('ember-qunit') 29 | .lt('5.0.0-beta.1'); 30 | 31 | this.options = this.options || {}; 32 | this.options.autoImport = { 33 | webpack: { 34 | module: { 35 | noParse: /\baxe\.js$/, 36 | }, 37 | }, 38 | }; 39 | 40 | // Ember-qunit < 5 provides an AMD shim for qunit but newer version now use 41 | // ember-auto-import to include qunit. This means that qunit is no 42 | // longer available for addons (if the parent app is using ember-qunit > 5) to 43 | // directly import under embroider unless they are using ember-auto-import 44 | // themselves. This conditionally falls back to not using ember-auto-import 45 | // when the parent app is providing qunit because without this we would double 46 | // include qunit resulting in a runtime error (qunit detects if it as 47 | // already be added to the window object and errors if so). 48 | if (hasMagicallyProvidedQUnit) { 49 | this.options.autoImport.exclude = ['qunit']; 50 | } 51 | }, 52 | 53 | /** 54 | * Adds content for the areas specified in the array above. It appends the 55 | * contents of the files with the same name to the content-for block. 56 | * @override 57 | */ 58 | contentFor: function (type) { 59 | if ( 60 | process.env.EMBER_ENV !== 'production' && 61 | ~ALLOWED_CONTENT_FOR.indexOf(type) 62 | ) { 63 | return fs.readFileSync( 64 | path.join(__dirname, 'content-for', type + '.html'), 65 | ); 66 | } 67 | }, 68 | 69 | /** 70 | * Allow the option to enable a11y audit and middelware reporter using environmental 71 | * variables. If set, environmental variable values are exposed to the browser 72 | * environment via `test-support/cli-options`. 73 | * @override 74 | */ 75 | treeForAddonTestSupport(tree) { 76 | const processedTree = new CliOptionsFilter(tree); 77 | return this._super.treeForAddonTestSupport.call(this, processedTree); 78 | }, 79 | 80 | ...setupMiddlewareHooks({ 81 | name: 'ember-a11y-testing', 82 | urlPath: 'report-violations', 83 | reportDir: 'ember-a11y-report', 84 | }), 85 | }; 86 | -------------------------------------------------------------------------------- /node-tests/setup-middleware-test.js: -------------------------------------------------------------------------------- 1 | const QUnit = require('qunit'); 2 | const fs = require('fs'); 3 | const tmp = require('tmp'); 4 | const express = require('express'); 5 | const readJSONSync = require('fs-extra').readJSONSync; 6 | const { 7 | setupMiddleware, 8 | } = require('@scalvert/ember-setup-middleware-reporter'); 9 | const violationsFixture = require('./fixtures/violations'); 10 | 11 | function createTmpDir() { 12 | return fs.realpathSync(tmp.dirSync({ unsafeCleanup: true }).name); 13 | } 14 | 15 | function buildResult(axeResults) { 16 | let { module, testName } = QUnit.config.current; 17 | 18 | return { 19 | moduleName: module.name, 20 | testName, 21 | helperName: 'visit', 22 | stack: 'STACK', 23 | axeResults, 24 | }; 25 | } 26 | 27 | QUnit.module('setupMiddleware', function (hooks) { 28 | let tmpDir; 29 | let app; 30 | let server; 31 | let port; 32 | 33 | hooks.beforeEach(async function () { 34 | tmpDir = createTmpDir(); 35 | app = express(); 36 | 37 | setupMiddleware(app, { 38 | root: tmpDir, 39 | name: 'ember-a11y-testing', 40 | urlPath: 'report-violations', 41 | reportDir: 'ember-a11y-report', 42 | }); 43 | // eslint-disable-next-line node/no-unsupported-features/es-syntax 44 | let { default: getPort } = await import('get-port'); 45 | 46 | port = await getPort({ port: 3000 }); 47 | server = app.listen(port); 48 | }); 49 | 50 | hooks.afterEach(function () { 51 | server.close(); 52 | }); 53 | 54 | QUnit.test( 55 | 'can respond to requests to report violations', 56 | async function (assert) { 57 | let data = [buildResult(violationsFixture)]; 58 | // eslint-disable-next-line node/no-unsupported-features/es-syntax 59 | let { default: fetch } = await import('node-fetch'); 60 | 61 | let json = await fetch(`http://localhost:${port}/report-violations`, { 62 | method: 'POST', 63 | headers: { 64 | 'Content-Type': 'application/json', 65 | }, 66 | body: JSON.stringify(data), 67 | }).then((res) => res.json()); 68 | 69 | assert.deepEqual(readJSONSync(json.outputPath), data); 70 | }, 71 | ); 72 | }); 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-a11y-testing", 3 | "version": "7.1.2", 4 | "description": "Accessibility testing for Ember applications", 5 | "keywords": [ 6 | "ember-addon", 7 | "accessibility", 8 | "ember", 9 | "a11y", 10 | "testing" 11 | ], 12 | "repository": "https://github.com/ember-a11y/ember-a11y-testing", 13 | "license": "MIT", 14 | "author": "Trent Willis ", 15 | "contributors": [ 16 | "Trent Willis ", 17 | "Andrew A Lee ", 18 | "Steve Calvert ", 19 | "Robert Jackson ", 20 | "BrianSipple ", 21 | "Melanie Sumner ", 22 | "Nathan Hammond ", 23 | "Sean Massa ", 24 | "Suz Hinton ", 25 | "Robert DeLuca ", 26 | "Sean Doyle ", 27 | "cibernox ", 28 | "Alicia Sedlock ", 29 | "Ondrej Sevcik ", 30 | "Sergey Astapov ", 31 | "Tomster ", 32 | "Marcy Sutton ", 33 | "Steve Zelaznik ", 34 | "Anthony Trama ", 35 | "Ken Sin ", 36 | "Francesco Novy ", 37 | "willibaur ", 38 | "Sarbbottam Bandyopadhyay ", 39 | "Eric Kelly ", 40 | "Sam Selikoff ", 41 | "Renato Iwashima " 42 | ], 43 | "directories": { 44 | "doc": "doc", 45 | "test": "tests" 46 | }, 47 | "scripts": { 48 | "build": "ember build --environment=production", 49 | "lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"", 50 | "lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix", 51 | "lint:hbs": "ember-template-lint .", 52 | "lint:hbs:fix": "ember-template-lint . --fix", 53 | "lint:js": "eslint . --cache", 54 | "lint:ts": "tsc --noEmit", 55 | "lint:js:fix": "eslint . --fix", 56 | "start": "ember serve", 57 | "test": "npm-run-all lint test:*", 58 | "test:node": "qunit node-tests", 59 | "test:ember": "ember test", 60 | "test:ember-compatibility": "ember try:each", 61 | "prepublishOnly": "ember ts:precompile", 62 | "postpublish": "ember ts:clean" 63 | }, 64 | "dependencies": { 65 | "@ember/test-waiters": "^2.4.3 || ^3.0.0 || ^4.0.0", 66 | "@glimmer/env": "^0.1.7", 67 | "@scalvert/ember-setup-middleware-reporter": "^0.1.1", 68 | "axe-core": "^4.10.3", 69 | "broccoli-persistent-filter": "^3.1.2", 70 | "ember-auto-import": "^2.2.4", 71 | "ember-cli-babel": "^8.2.0", 72 | "ember-cli-htmlbars": "^6.3.0", 73 | "ember-cli-typescript": "^4.2.1", 74 | "ember-cli-version-checker": "^5.1.2", 75 | "fs-extra": "^11.2.0", 76 | "validate-peer-dependencies": "^2.0.0" 77 | }, 78 | "devDependencies": { 79 | "@ember/optional-features": "^2.0.0", 80 | "@ember/string": "^4.0.1", 81 | "@ember/test-helpers": "^3.0.3", 82 | "@embroider/test-setup": "^4.0.0", 83 | "@glimmer/component": "^1.0.4", 84 | "@glimmer/tracking": "^1.0.4", 85 | "@types/ember": "^3.16.0", 86 | "@types/ember-qunit": "^3.4.15", 87 | "@types/ember__destroyable": "^4.0.5", 88 | "@types/qunit": "^2.11.3", 89 | "@types/rsvp": "^4.0.3", 90 | "@typescript-eslint/eslint-plugin": "^4.0.0", 91 | "@typescript-eslint/parser": "^4.0.0", 92 | "babel-eslint": "^10.1.0", 93 | "broccoli-asset-rev": "^3.0.0", 94 | "ember-cli": "^4.0.1", 95 | "ember-cli-dependency-checker": "^3.2.0", 96 | "ember-cli-github-pages": "^0.2.2", 97 | "ember-cli-inject-live-reload": "^2.1.0", 98 | "ember-cli-sass": "^11.0.1", 99 | "ember-cli-sri": "^2.1.1", 100 | "ember-cli-terser": "^4.0.2", 101 | "ember-cli-typescript-blueprints": "^3.0.0", 102 | "ember-disable-prototype-extensions": "^1.1.3", 103 | "ember-load-initializers": "^2.1.2", 104 | "ember-qunit": "^7.0.0", 105 | "ember-resolver": "^13.1.0", 106 | "ember-sinon": "^5.0.0", 107 | "ember-source": "^4.0.1", 108 | "ember-source-channel-url": "^3.0.0", 109 | "ember-template-imports": "^4.3.0", 110 | "ember-template-lint": "^7.0.4", 111 | "ember-truth-helpers": "^3.0.0", 112 | "ember-try": "^4.0.0", 113 | "eslint": "^7.32.0", 114 | "eslint-config-prettier": "^10.1.3", 115 | "eslint-plugin-ember": "^10.5.8", 116 | "eslint-plugin-node": "^11.1.0", 117 | "eslint-plugin-prettier": "^5.2.1", 118 | "eslint-plugin-qunit": "^7.2.0", 119 | "express": "^5.1.0", 120 | "get-port": "^6.0.0", 121 | "loader.js": "^4.7.0", 122 | "node-fetch": "^3.1.0", 123 | "npm-run-all": "^4.1.5", 124 | "prettier": "^3.3.3", 125 | "qunit": "^2.17.2", 126 | "qunit-dom": "^2.0.0", 127 | "release-plan": "^0.16.0", 128 | "sass": "^1.26.10", 129 | "tmp": "^0.2.1", 130 | "typescript": "^5.5.4", 131 | "webpack": "^5.65.0" 132 | }, 133 | "peerDependencies": { 134 | "@ember/test-helpers": "^3.0.3 || ^4.0.2 || ^5.0.0", 135 | "qunit": ">= 2" 136 | }, 137 | "peerDependenciesMeta": { 138 | "qunit": { 139 | "optional": true 140 | } 141 | }, 142 | "packageManager": "pnpm@10.8.0+sha512.0e82714d1b5b43c74610193cb20734897c1d00de89d0e18420aebc5977fa13d780a9cb05734624e81ebd81cc876cd464794850641c48b9544326b5622ca29971", 143 | "engines": { 144 | "node": "16.* || >= 18" 145 | }, 146 | "publishConfig": { 147 | "registry": "https://registry.npmjs.org" 148 | }, 149 | "ember": { 150 | "edition": "octane" 151 | }, 152 | "ember-addon": { 153 | "demoURL": "http://ember-a11y.github.io/ember-a11y-testing", 154 | "configPath": "tests/dummy/config" 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_start_timeout: 120, 9 | browser_args: { 10 | Chrome: { 11 | ci: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.CI ? '--no-sandbox' : null, 14 | '--headless', 15 | '--disable-dev-shm-usage', 16 | '--disable-software-rasterizer', 17 | '--mute-audio', 18 | '--remote-debugging-port=0', 19 | '--window-size=1440,900', 20 | ].filter(Boolean), 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /tests/acceptance/a11y-audit-test.ts: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { visit } from '@ember/test-helpers'; 3 | import { setupApplicationTest } from 'ember-qunit'; 4 | import { 5 | a11yAudit, 6 | setEnableA11yAudit, 7 | setRunOptions, 8 | } from 'ember-a11y-testing/test-support'; 9 | 10 | const SELECTORS = { 11 | passingComponent: '[data-test-selector="violations-page__passing-component"]', 12 | failingComponent: '[data-test-selector="empty-button"]', 13 | }; 14 | 15 | module('Acceptance | a11y audit', function (hooks) { 16 | setupApplicationTest(hooks); 17 | 18 | hooks.beforeEach(function () { 19 | setEnableA11yAudit(true); 20 | }); 21 | 22 | hooks.afterEach(function () { 23 | setEnableA11yAudit(false); 24 | }); 25 | 26 | test('a11yAudit should catch violations as an async helper', async function (assert) { 27 | assert.expect(1); 28 | 29 | await visit('/'); 30 | 31 | await assert.rejects( 32 | >a11yAudit(), 33 | /The page should have no accessibility violations. Violations:/, 34 | 'error message is correct', 35 | ); 36 | }); 37 | 38 | test('a11yAudit should properly scope to a specified string context selector', async function (assert) { 39 | assert.expect(2); 40 | 41 | await visit('/'); 42 | 43 | await a11yAudit(SELECTORS.passingComponent); 44 | assert.ok(true, 'a11yAudit should not have discovered any issues'); 45 | 46 | await assert.rejects( 47 | >a11yAudit(SELECTORS.failingComponent), 48 | /The page should have no accessibility violations. Violations:/, 49 | 'error message is correct', 50 | ); 51 | }); 52 | 53 | test('a11yAudit should properly scope to a specified jquery context (not recommended)', async function (assert) { 54 | await visit('/'); 55 | 56 | await a11yAudit(SELECTORS.passingComponent); 57 | assert.ok(true, 'a11yAudit should not have discovered any issues'); 58 | }); 59 | 60 | test('a11yAudit should properly scope to a specified html element context (not recommended)', async function (assert) { 61 | await visit('/'); 62 | 63 | await a11yAudit(SELECTORS.passingComponent); 64 | assert.ok(true, 'a11yAudit should not have discovered any issues'); 65 | }); 66 | 67 | test('a11yAudit accounts for axe.run include and exclude context parameter', async function (assert) { 68 | setRunOptions({ 69 | rules: { 70 | // Disabled to test whether the config is 71 | // properly loaded in test environment 72 | 'image-alt': { enabled: false }, 73 | }, 74 | }); 75 | 76 | await visit('/'); 77 | 78 | await a11yAudit({ 79 | include: [[SELECTORS.passingComponent]], 80 | }); 81 | 82 | await a11yAudit({ 83 | include: [['#ember-testing-container']], 84 | exclude: [ 85 | [SELECTORS.failingComponent], 86 | ['[data-test-selector="labeless-text-input"]'], 87 | ['[data-test-selector="paragraph-with-blink-tag"]'], 88 | ['[data-test-selector="ungrouped-radio-inputs"]'], 89 | ['[data-test-selector="noise-level-selection"]'], 90 | ['[data-test-selector="poor-text-contrast"]'], 91 | ], 92 | }); 93 | 94 | assert.ok(true, 'no errors should have been found in a11yAudit'); 95 | }); 96 | 97 | test('a11yAudit can accept an options hash in addition to context', async function (assert) { 98 | await visit('/'); 99 | 100 | await a11yAudit(SELECTORS.failingComponent, { 101 | rules: { 102 | 'button-name': { 103 | enabled: false, 104 | }, 105 | }, 106 | }); 107 | 108 | assert.ok(true, 'no errors should have been found in a11yAudit'); 109 | }); 110 | 111 | test('a11yAudit can accept an options hash as a single argument', async function (assert) { 112 | await visit('/'); 113 | 114 | await a11yAudit({ 115 | runOnly: { 116 | type: 'rule', 117 | values: ['accesskeys'], 118 | }, 119 | }); 120 | 121 | assert.ok(true, 'no errors should have been found in a11yAudit'); 122 | }); 123 | 124 | test('a11yAudit loads default config if none specified', async function (assert) { 125 | await visit('/ignored-image-alt'); 126 | 127 | setRunOptions({ 128 | rules: { 129 | // Disabled to test whether the config is 130 | // properly loaded in test environment 131 | 'image-alt': { enabled: false }, 132 | }, 133 | }); 134 | 135 | // There is an error with img alt tag, but it's ignored in global config 136 | await a11yAudit(); 137 | 138 | assert.ok(true, 'the image-alt rule should be ignored'); 139 | }); 140 | }); 141 | -------------------------------------------------------------------------------- /tests/acceptance/reporter-test.ts: -------------------------------------------------------------------------------- 1 | import { AxeResults } from 'axe-core'; 2 | import { module, test } from 'qunit'; 3 | import { visit } from '@ember/test-helpers'; 4 | import { setupApplicationTest } from 'ember-qunit'; 5 | import { a11yAudit, setCustomReporter } from 'ember-a11y-testing/test-support'; 6 | 7 | module('reporter', function (hooks) { 8 | setupApplicationTest(hooks); 9 | 10 | hooks.afterEach(function () { 11 | setCustomReporter(); // reset to default value 12 | }); 13 | 14 | test('setCustomReporter can correctly set a custom reporter in favor of default', async function (assert) { 15 | assert.expect(1); 16 | 17 | setCustomReporter(async (axeResult: AxeResults) => { 18 | assert.strictEqual(axeResult.violations.length, 5); 19 | }); 20 | 21 | await visit('/'); 22 | 23 | await a11yAudit(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/acceptance/setup-global-a11y-hooks-test.ts: -------------------------------------------------------------------------------- 1 | import QUnit, { module, test } from 'qunit'; 2 | import { 3 | setupGlobalA11yHooks, 4 | teardownGlobalA11yHooks, 5 | setEnableA11yAudit, 6 | } from 'ember-a11y-testing/test-support'; 7 | import { setupApplicationTest } from 'ember-qunit'; 8 | import { visit } from '@ember/test-helpers'; 9 | import { InvocationStrategy } from 'ember-a11y-testing/test-support/types'; 10 | 11 | function getRange(count: number) { 12 | return Array(count) 13 | .fill(0) 14 | .map((_, i) => i + 1); 15 | } 16 | 17 | function getTestName(n: number, count: number, invocationStrategy: string) { 18 | return n === count 19 | ? `it invokes correctly using ${invocationStrategy} invocation strategy` 20 | : `IGNORE: test used to validate setupGlobalA11yHooks (${n})`; 21 | } 22 | 23 | module('setupGlobalA11yHooks with invokeAll', function (hooks) { 24 | setupApplicationTest(hooks); 25 | 26 | const TEST_INVOCATIONS_COUNT = 6; 27 | const EXPECTED_AUDIT_INVOCATIONS_COUNT = 6; 28 | let actualAuditInvocationsCount = 0; 29 | let numInvoked = 0; 30 | 31 | function invokeAll(): boolean { 32 | return true; 33 | } 34 | 35 | async function a11yAuditFake() { 36 | actualAuditInvocationsCount += 1; 37 | } 38 | 39 | hooks.before(function () { 40 | setupGlobalA11yHooks(invokeAll, a11yAuditFake); 41 | setEnableA11yAudit(true); 42 | }); 43 | 44 | hooks.after(function () { 45 | teardownGlobalA11yHooks(); 46 | setEnableA11yAudit(); 47 | }); 48 | 49 | getRange(TEST_INVOCATIONS_COUNT).forEach((num) => { 50 | test( 51 | getTestName(num, TEST_INVOCATIONS_COUNT, 'invokeAll'), 52 | async function (assert) { 53 | assert.expect(0); 54 | 55 | await visit('/'); 56 | 57 | numInvoked++; 58 | 59 | if (numInvoked === TEST_INVOCATIONS_COUNT) { 60 | assert.expect(1); 61 | // eslint-disable-next-line qunit/no-conditional-assertions 62 | assert.strictEqual( 63 | actualAuditInvocationsCount, 64 | EXPECTED_AUDIT_INVOCATIONS_COUNT, 65 | ); 66 | } 67 | }, 68 | ); 69 | }); 70 | }); 71 | 72 | module('setupGlobalA11yHooks with invokeEveryN', function (hooks) { 73 | setupApplicationTest(hooks); 74 | 75 | const TEST_INVOCATIONS_COUNT = 6; 76 | const EXPECTED_AUDIT_INVOCATIONS_COUNT = 2; 77 | let actualAuditInvocationsCount = 0; 78 | let numInvoked = 0; 79 | 80 | function invokeEveryN(interval: number): InvocationStrategy { 81 | let invocationCounter: number = 0; 82 | 83 | return () => { 84 | invocationCounter++; 85 | 86 | return invocationCounter % interval === 0; 87 | }; 88 | } 89 | 90 | async function a11yAuditFake() { 91 | actualAuditInvocationsCount += 1; 92 | } 93 | 94 | hooks.before(function () { 95 | setupGlobalA11yHooks(invokeEveryN(3), a11yAuditFake); 96 | setEnableA11yAudit(true); 97 | }); 98 | 99 | hooks.after(function () { 100 | teardownGlobalA11yHooks(); 101 | setEnableA11yAudit(); 102 | }); 103 | 104 | getRange(TEST_INVOCATIONS_COUNT).forEach((num) => { 105 | test( 106 | getTestName(num, TEST_INVOCATIONS_COUNT, 'invokeEveryN'), 107 | async function (assert) { 108 | assert.expect(0); 109 | 110 | await visit('/'); 111 | 112 | numInvoked++; 113 | 114 | if (numInvoked === TEST_INVOCATIONS_COUNT) { 115 | assert.expect(1); 116 | // eslint-disable-next-line qunit/no-conditional-assertions 117 | assert.strictEqual( 118 | actualAuditInvocationsCount, 119 | EXPECTED_AUDIT_INVOCATIONS_COUNT, 120 | ); 121 | } 122 | }, 123 | ); 124 | }); 125 | }); 126 | 127 | module('setupGlobalA11yHooks with invokeWithExclusions', function (hooks) { 128 | setupApplicationTest(hooks); 129 | 130 | const TEST_INVOCATIONS_COUNT = 6; 131 | let actualTestsRun: string[] = []; 132 | let numInvoked = 0; 133 | 134 | function invokeWithExclusions(): boolean { 135 | const EXCLUDED_TESTS = [ 136 | 'IGNORE: test used to validate setupGlobalA11yHooks (2)', 137 | 'it invokes correctly using invokeWithExclusions invocation strategy', 138 | ]; 139 | 140 | return !EXCLUDED_TESTS.includes(QUnit.config.current.testName); 141 | } 142 | 143 | async function a11yAuditFake() { 144 | actualTestsRun.push(QUnit.config.current.testName); 145 | } 146 | 147 | hooks.before(function () { 148 | setupGlobalA11yHooks(invokeWithExclusions, a11yAuditFake); 149 | setEnableA11yAudit(true); 150 | }); 151 | 152 | hooks.after(function () { 153 | teardownGlobalA11yHooks(); 154 | setEnableA11yAudit(); 155 | }); 156 | 157 | getRange(TEST_INVOCATIONS_COUNT).forEach((num) => { 158 | test( 159 | getTestName(num, TEST_INVOCATIONS_COUNT, 'invokeWithExclusions'), 160 | async function (assert) { 161 | assert.expect(0); 162 | 163 | await visit('/'); 164 | 165 | numInvoked++; 166 | 167 | if (numInvoked === TEST_INVOCATIONS_COUNT) { 168 | assert.expect(1); 169 | // eslint-disable-next-line qunit/no-conditional-assertions 170 | assert.deepEqual(actualTestsRun, [ 171 | 'IGNORE: test used to validate setupGlobalA11yHooks (1)', 172 | 'IGNORE: test used to validate setupGlobalA11yHooks (3)', 173 | 'IGNORE: test used to validate setupGlobalA11yHooks (4)', 174 | 'IGNORE: test used to validate setupGlobalA11yHooks (5)', 175 | ]); 176 | } 177 | }, 178 | ); 179 | }); 180 | }); 181 | -------------------------------------------------------------------------------- /tests/acceptance/setup-middleware-reporter-test.ts: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupApplicationTest } from 'ember-qunit'; 3 | import { visit } from '@ember/test-helpers'; 4 | import { 5 | setCustomReporter, 6 | setEnableA11yAudit, 7 | setupGlobalA11yHooks, 8 | teardownGlobalA11yHooks, 9 | _middlewareReporter, 10 | _pushTestResult, 11 | _TEST_SUITE_RESULTS, 12 | } from 'ember-a11y-testing/test-support'; 13 | 14 | module('setupMiddlewareReporter', function (hooks) { 15 | setupApplicationTest(hooks); 16 | 17 | function invokeAll(): boolean { 18 | return true; 19 | } 20 | 21 | hooks.beforeEach(function () { 22 | setCustomReporter(_middlewareReporter); 23 | setupGlobalA11yHooks(invokeAll); 24 | setEnableA11yAudit(true); 25 | }); 26 | 27 | hooks.afterEach(function () { 28 | setCustomReporter(); 29 | teardownGlobalA11yHooks(); 30 | setEnableA11yAudit(); 31 | }); 32 | 33 | test('gathers results from failed a11yAudit calls', async function (assert) { 34 | assert.expect(1); 35 | 36 | await visit('/'); 37 | 38 | _pushTestResult(); 39 | 40 | assert.deepEqual(_TEST_SUITE_RESULTS[0].violations.length, 5); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'dummy/config/environment'; 5 | 6 | export default class App extends Application { 7 | modulePrefix = config.modulePrefix; 8 | podModulePrefix = config.podModulePrefix; 9 | Resolver = Resolver; 10 | } 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/components/axe-component.hbs: -------------------------------------------------------------------------------- 1 |
{{yield}}
2 | -------------------------------------------------------------------------------- /tests/dummy/app/components/passing-component.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /tests/dummy/app/components/violations-grid-item.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 |
    3 |

    {{@title}}

    4 |
    5 | 6 |
    7 |
    8 | {{!-- Provide violating content here --}} 9 | {{yield}} 10 |
    11 |
    12 |
  • 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/violations-grid-item.js: -------------------------------------------------------------------------------- 1 | // Temporary until Ember 3.8 & 3.12 support is dropped, 2 | // afterwhich can be replaced w/templateOnly. 3 | /* eslint-disable ember/no-empty-glimmer-component-classes */ 4 | import Component from '@glimmer/component'; 5 | 6 | export default class ViolationsGridItem extends Component {} 7 | -------------------------------------------------------------------------------- /tests/dummy/app/components/x-paragraph.hbs: -------------------------------------------------------------------------------- 1 |

    {{yield}}

    2 | -------------------------------------------------------------------------------- /tests/dummy/app/components/x-paragraph.js: -------------------------------------------------------------------------------- 1 | // Temporary until Ember 3.8 & 3.12 support is dropped, 2 | // afterwhich can be replaced w/`templateOnly()`. 3 | /* eslint-disable ember/no-empty-glimmer-component-classes */ 4 | import Component from '@glimmer/component'; 5 | 6 | export default class XParagraph extends Component {} 7 | -------------------------------------------------------------------------------- /tests/dummy/app/config/environment.d.ts: -------------------------------------------------------------------------------- 1 | export default config; 2 | 3 | /** 4 | * Type declarations for 5 | * import config from './config/environment' 6 | * 7 | * For now these need to be managed by the developer 8 | * since different ember addons can materialize new entries. 9 | */ 10 | declare const config: { 11 | environment: any; 12 | modulePrefix: string; 13 | podModulePrefix: string; 14 | locationType: string; 15 | rootURL: string; 16 | }; 17 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'dummy/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () { 10 | this.route('violations', { path: '/' }); 11 | this.route('ignored-image-alt'); 12 | }); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/routes/ignored-image-alt.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | export default class IgnoreImageAltRoute extends Route {} 4 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/index.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | export default class IndexRoute extends Route {} 4 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/violations.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | 3 | export default class ViolationsRoute extends Route {} 4 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | .button { 2 | background-color: aqua; 3 | border-radius: 0.25em; 4 | border: none; 5 | min-height: 1.5rem; 6 | height: 1.5rem; 7 | 8 | width: 2.2rem; 9 | min-width: 2.2rem; 10 | } 11 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.scss: -------------------------------------------------------------------------------- 1 | @use 'variables/variables' as *; 2 | @use 'globals' as *; 3 | 4 | .application { 5 | position: relative; 6 | width: 100%; 7 | height: 100vh; 8 | overflow: hidden; 9 | overflow-y: auto; 10 | background-color: $theme-color__offWhite; 11 | z-index: 1; 12 | 13 | .application__main-content { 14 | min-width: $min-width__main-content; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/components/_button.scss: -------------------------------------------------------------------------------- 1 | @use '../variables/variables' as *; 2 | 3 | .c-button { 4 | display: flex; 5 | font-size: 1em; 6 | padding: $padding__content-box; 7 | border-radius: 0.125em; 8 | color: $theme-color__nearWhite; 9 | border: 0; 10 | cursor: pointer; 11 | background-color: $theme-color__primary-1--500; 12 | transition-property: background-color; 13 | transition-duration: 0.25s; 14 | transition-timing-function: $easing__ease-out-cubic; 15 | 16 | &:hover { 17 | background-color: $theme-color__primary-1--300; 18 | transition-timing-function: $easing__ease-in-quad; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/components/_components.scss: -------------------------------------------------------------------------------- 1 | @use 'button'; 2 | @use 'image'; 3 | @use 'menu-link-item'; 4 | @use 'radio-track'; 5 | @use 'violations-grid-item'; 6 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/components/_image.scss: -------------------------------------------------------------------------------- 1 | .c-img { 2 | object-fit: cover; 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/components/_menu-link-item.scss: -------------------------------------------------------------------------------- 1 | @use '../variables/variables' as *; 2 | 3 | .c-menu-link-item { 4 | height: 3.25em; 5 | position: relative; 6 | 7 | &::before, 8 | &::after { 9 | content: ''; 10 | position: absolute; 11 | top: 0; 12 | left: 0; 13 | width: 100%; 14 | height: 100%; 15 | opacity: 0; 16 | z-index: -1; 17 | transition-property: opacity; 18 | transition-duration: $duration__fade-toggle; 19 | transition-timing-function: $easing__ease-out-cubic; 20 | } 21 | 22 | &:hover:not(.is-active)::before, 23 | &:focus:not(.is-active)::before { 24 | opacity: 1; 25 | transition-timing-function: $easing__ease-in-quad; 26 | } 27 | 28 | &.is-active::after { 29 | opacity: 1; 30 | } 31 | 32 | .c-menu-link-item__link { 33 | display: flex; 34 | align-items: center; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/components/_radio-track.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:math'; 2 | @use '../variables/variables' as *; 3 | 4 | $height__radio-selector-track-container: 4rem; 5 | $height__radio-selector-track-circle: $height__radio-selector-track-container * 6 | 0.5; 7 | $offset__title-below-circle: 0.4em; 8 | 9 | $color__circle--primary: $theme-color__primary-1--700; 10 | $color__circle--accent: $theme-color__primary-1--100; 11 | $color__label--checked: $theme-color__primary-1--300; 12 | $color__track: hsla(0, 0%, 59%, 1); 13 | $color__track-heading: $theme-color__primary-1--700; 14 | 15 | $animation-easing__to-checked: cubic-bezier( 16 | 0, 17 | 0.3, 18 | 0, 19 | 1.48 20 | ); /* ease-out-cubic with 8% bounce */ 21 | $animation-easing__to-unchecked: cubic-bezier( 22 | 0.47, 23 | 0, 24 | 0.745, 25 | 0.715 26 | ); /* ease-in-sine */ 27 | $animation-duration__circle-toggle: 0.37s; 28 | 29 | .c-radio-track { 30 | border: 0; 31 | } 32 | 33 | .c-radio-track__heading { 34 | font-size: 1.67em; 35 | font-weight: bold; 36 | padding-bottom: 0.5em; 37 | color: $color__track-heading; 38 | } 39 | 40 | /** 41 | * We'll treat the the container for our radio items as the "line", 42 | * and make the track from a background linear gradient 43 | */ 44 | .c-radio-track__line { 45 | margin: 0; 46 | height: $height__radio-selector-track-container; 47 | background-image: linear-gradient( 48 | to bottom, 49 | transparent 0%, 50 | transparent 48%, 51 | $color__track 48%, 52 | $color__track 52%, 53 | transparent 52% 54 | ); 55 | display: flex; 56 | align-items: center; 57 | justify-content: space-around; 58 | } 59 | 60 | /** 61 | * The containing element for each item, which consists of a wrapping 62 | * label, the radio input, and a span that will comprise the label text, 63 | * and shape of the indicators (using the spans ::after and ::before psuedo-elements) 64 | */ 65 | .c-radio-track__radio-item { 66 | width: $height__radio-selector-track-circle; 67 | height: $height__radio-selector-track-circle; 68 | cursor: pointer; 69 | border-radius: 50%; 70 | position: relative; 71 | background-color: transparent; 72 | } 73 | 74 | .c-radio-track__item-input { 75 | clip: rect(0 0 0 0); 76 | height: 1px; 77 | width: 1px; 78 | margin: -1px; 79 | position: absolute; 80 | padding: 0; 81 | overflow: hidden; 82 | border: 0; 83 | } 84 | 85 | /** 86 | * Each item will have a "pointer" and a "node" (the circle and a text title) 87 | * which comprises its visual appearance a title will sit below it 88 | */ 89 | .c-radio-track__item-pointer, 90 | .c-radio-track__item-node { 91 | position: absolute; 92 | transform: translateX(-50%); 93 | } 94 | 95 | .c-radio-track__item-node { 96 | font-weight: bold; 97 | color: $color__track; 98 | white-space: nowrap; 99 | //pointer-events: none; /* Have the pointer-events handled by the ::before element */ 100 | 101 | /* Push the title down below the psuedo-elements */ 102 | top: calc( 103 | #{$offset__title-below-circle} + #{$height__radio-selector-track-circle} 104 | ); 105 | transition-property: color; 106 | } 107 | 108 | .c-radio-track__item-pointer { 109 | position: absolute; 110 | color: $color__label--checked; 111 | margin-top: calc( 112 | -1 * (#{math.div($height--radio-selector-track-circle, 2)} + 1.5em) 113 | ); 114 | } 115 | 116 | .c-radio-track__item-pointer::after { 117 | font-size: 1.5em; 118 | opacity: 0; 119 | transform: scale(0); 120 | content: '\0020\2193'; 121 | } 122 | 123 | /** 124 | * Set up our circles 125 | */ 126 | .c-radio-track__item-node::after, 127 | .c-radio-track__item-node::before { 128 | content: ''; 129 | position: absolute; 130 | top: 0; 131 | left: 50%; 132 | margin-left: -1 * (math.div($height--radio-selector-track-circle, 2)); 133 | width: $height__radio-selector-track-circle; 134 | height: $height__radio-selector-track-circle; 135 | border-radius: 50%; 136 | margin-top: calc( 137 | -1 * #{$height__radio-selector-track-circle} - #{$offset__title-below-circle} 138 | ); 139 | } 140 | 141 | /** 142 | * The ::before circle will be visible in the "unchecked" state 143 | */ 144 | .c-radio-track__item-node::before { 145 | opacity: 1; 146 | transform: scale(1); 147 | background-color: $color__circle--accent; 148 | transition-delay: 0; 149 | //pointer-events: all; 150 | } 151 | 152 | /** 153 | * The ::after circle will animate over the ::before circle 154 | * when the radio becomes checked 155 | */ 156 | .c-radio-track__item-node::after { 157 | opacity: 0; 158 | transform: scale(0); 159 | cursor: default; 160 | background-image: radial-gradient( 161 | circle, 162 | $color__circle--primary 0%, 163 | $color__circle--primary 164 | ); 165 | box-shadow: 0 0 0 0.25em $color__circle--accent, 166 | 0 0 0 0.5em $color__circle--primary; 167 | } 168 | 169 | /* --------------------- Animations & event handling ----------------- */ 170 | .c-radio-track__item-pointer::after, 171 | .c-radio-track__item-node::after, 172 | .c-radio-track__item-node::before { 173 | transition-property: opacity, transform, background-color; 174 | transition-duration: $animation-duration__circle-toggle; 175 | transition-timing-function: $animation-easing__to-unchecked; 176 | } 177 | 178 | /** 179 | * Define animations for the sibling of the input when the input 180 | * becomes checked 181 | */ 182 | .c-radio-track__item-input:checked + .c-radio-track__item-node { 183 | color: $color__label--checked; 184 | 185 | &::after { 186 | opacity: 1; 187 | transform: scale(1); 188 | transition-timing-function: $animation-easing__to-checked; 189 | } 190 | &::before { 191 | opacity: 0; 192 | transition-delay: $animation-duration__circle-toggle; 193 | } 194 | } 195 | 196 | .c-radio-track__item-input:focus ~ .c-radio-track__item-pointer::after { 197 | opacity: 1; 198 | transform: scale(1); 199 | transition-timing-function: $animation-easing__to-checked; 200 | } 201 | 202 | /** 203 | * Hovering over circle when it's not focused or checked 204 | */ 205 | .c-radio-track__item-input:hover:not(:focus):not(:checked) 206 | + .c-radio-track__item-node::before { 207 | background-color: $theme-color__primary-1--500; 208 | } 209 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/components/_violations-grid-item.scss: -------------------------------------------------------------------------------- 1 | @use '../variables/variables' as *; 2 | 3 | .c-violations-grid-item { 4 | box-shadow: $box-shadow__card; 5 | 6 | .c-violations-grid-item__header { 7 | height: 20%; 8 | } 9 | 10 | .c-violations-grid-item__body { 11 | height: 80%; 12 | } 13 | 14 | .c-violations-grid-item__header, 15 | .c-violations-grid-item__body { 16 | overflow: hidden; 17 | } 18 | 19 | .c-violations-grid-item__body-content { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | flex-wrap: wrap; 24 | } 25 | 26 | .c-violations-grid-item__title { 27 | //border-bottom: 1px solid; 28 | margin-bottom: 2em; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/globals.scss: -------------------------------------------------------------------------------- 1 | @use 'components/components'; 2 | @use 'object-patterns/object-patterns'; 3 | @use 'pages/pages'; 4 | @use 'typography/typography'; 5 | @use 'utilities/utilities'; 6 | @use 'variables/variables' as *; 7 | 8 | html { 9 | font-size: 100%; 10 | } 11 | 12 | body { 13 | position: relative; 14 | margin: 0; 15 | padding: 0; 16 | width: 100%; 17 | height: 100%; 18 | background-color: $theme-color__offWhite; 19 | } 20 | 21 | ul { 22 | list-style-type: none; 23 | margin: 0; 24 | padding: 0; 25 | } -------------------------------------------------------------------------------- /tests/dummy/app/styles/object-patterns/_content-box.scss: -------------------------------------------------------------------------------- 1 | @use '../variables/variables' as *; 2 | 3 | .o-content-box { 4 | box-sizing: border-box; 5 | padding: $padding__content-box; 6 | } 7 | 8 | .o-content-box--lg { 9 | box-sizing: border-box; 10 | padding: $padding__content-box--lg; 11 | } 12 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/object-patterns/_menu-link.scss: -------------------------------------------------------------------------------- 1 | .o-menu-link { 2 | text-decoration: none; 3 | color: inherit; 4 | cursor: pointer; 5 | } 6 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/object-patterns/_object-patterns.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * "Objects" (prefixed with "o-") that are meant to 3 | * comprise lightweight, abstract, composable patterns 4 | * @see: https://www.google.com/search?q=object+orieinted+css&oq=object+orieinted+css&aqs=chrome..69i57j0l5.3247j0j7&sourceid=chrome&ie=UTF-8 5 | */ 6 | 7 | @use 'content-box'; 8 | @use 'menu-link'; 9 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/pages/_pages.scss: -------------------------------------------------------------------------------- 1 | @use 'violations'; 2 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/pages/_violations.scss: -------------------------------------------------------------------------------- 1 | @use '../variables/variables' as *; 2 | 3 | 4 | .p-violations__header, 5 | .p-violations__noise-selectors, 6 | .p-violations__body { 7 | margin-bottom: 2rem; 8 | } 9 | 10 | .p-violations__header-hint { 11 | color: $theme-color__primary-1--700; 12 | } 13 | 14 | .p-violations__noise-selectors-container { 15 | border: 0; 16 | } 17 | 18 | .p-violations__grid { 19 | max-width: 100%; 20 | display: flex; 21 | flex-wrap: wrap; 22 | align-items: center; 23 | justify-content: space-around; 24 | flex-direction: row; 25 | } 26 | 27 | .p-violations__grid-item { 28 | position: relative; 29 | width: $width__grid-item; 30 | flex-grow: 0; 31 | flex-shrink: 1; 32 | height: $height__grid-item; 33 | background-color: $theme-color__nearWhite; 34 | margin: $margin__grid-item--vertical $margin__grid-item--horizontal; 35 | } 36 | 37 | @media screen and (max-width: $min-width__main-content) { 38 | .p-violations__grid { 39 | flex-direction: column; 40 | } 41 | .p-violations__grid-item, 42 | .p-violations__header-hint { 43 | width: 100%; 44 | } 45 | } 46 | 47 | .p-violations__grid-item-content--low-contrast-text { 48 | color: yellow; 49 | } 50 | 51 | .p-violations__grid-item-content--radio-items { 52 | display: flex; 53 | justify-content: center; 54 | align-items: center; 55 | flex-direction: column; 56 | 57 | input { 58 | float: left; 59 | margin-right: 1em; 60 | } 61 | } 62 | //.p-violations__grid-item-content--radio { 63 | // flex-shrink: 0; 64 | // 65 | // input { 66 | // float: left; 67 | // } 68 | //} 69 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/typography/_fonts.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Target the system fonts first (http://www.smashingmagazine.com/2015/11/using-system-ui-fonts-practical-guide/) 3 | * then provide similar fallbacks 4 | */ 5 | 6 | html { 7 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Oxygen', 8 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', Avenir, Geneva, Tahoma, 9 | san-serif; 10 | } 11 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/typography/_headings.scss: -------------------------------------------------------------------------------- 1 | @use '../variables/variables' as *; 2 | 3 | h1, 4 | h2, 5 | h3, 6 | h4, 7 | h5, 8 | h6 { 9 | margin: 0; 10 | color: $theme-color__primary-1--500; 11 | line-height: 2.25; 12 | } 13 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/typography/_typography.scss: -------------------------------------------------------------------------------- 1 | @use 'fonts'; 2 | @use 'headings'; 3 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/utilities/_utilities.scss: -------------------------------------------------------------------------------- 1 | .u-relative { 2 | position: relative; 3 | } 4 | .u-fill-height { 5 | height: 100%; 6 | } 7 | .u-fill-width { 8 | width: 100%; 9 | } 10 | 11 | .u-fill { 12 | height: 100%; 13 | width: 100%; 14 | } 15 | 16 | .u-align-center { 17 | text-align: center; 18 | } 19 | 20 | .u-margin-auto { 21 | margin: auto; 22 | } 23 | .u-block { 24 | display: block; 25 | } 26 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/variables/_variables.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * ----------------------------------------- 3 | * COLORS 4 | * ----------------------------------------- 5 | */ 6 | $theme-color__primary-1--100: hsla(251, 72%, 81%, 1); 7 | $theme-color__primary-1--300: hsla(261, 43%, 55%, 1); 8 | $theme-color__primary-1--500: hsla(261, 43%, 45%, 1); 9 | $theme-color__primary-1--700: hsla(251, 42%, 31%, 1); 10 | $theme-color__primary-1--900: hsla(256, 22%, 10%, 1); 11 | 12 | $theme-color__nearWhite: hsla(0, 0%, 98%, 1); 13 | $theme-color__offWhite: hsla(0, 0%, 91%, 1); 14 | 15 | $color__box-shadow: hsla(0, 0%, 0%, 0.43); 16 | 17 | $box-shadow__card: 0 3px 12px rgba(0, 0, 0, 0.23), 18 | 0 3px 12px rgba(0, 0, 0, 0.16); 19 | 20 | /** 21 | * ----------------------------------------- 22 | * ANIMATION 23 | * ----------------------------------------- 24 | */ 25 | $duration__fade-toggle: 0.25s; 26 | 27 | $easing__ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53); 28 | $easing__ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1); 29 | 30 | /** 31 | * ----------------------------------------- 32 | * DIMENSIONS 33 | * ----------------------------------------- 34 | */ 35 | 36 | /* 37 | * 180:130 padding ratio to align content aspect ratio with human FOV 38 | */ 39 | $padding__content-box: 0.75em 1.05em; 40 | $padding__content-box--lg: 1.5em 2.1em; 41 | 42 | /* This could be a breakpoint in, you know, a real app 😜 */ 43 | $min-width__main-content: 40rem; 44 | 45 | 46 | $width__grid-item: 24rem; 47 | $height__grid-item: $width__grid-item * 0.55; 48 | $margin__grid-item--vertical: 0.5em; 49 | $margin__grid-item--horizontal: 0.75em; 50 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 |

    Ember A11y Testing

    5 |
    6 | 7 |
    8 | 9 | {{!-- Main content --}} 10 | {{outlet}} 11 | 12 |
    13 |
    14 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ignored-image-alt.hbs: -------------------------------------------------------------------------------- 1 | {{!-- template-lint-disable require-valid-alt-text --}} 2 | 3 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/violations.hbs: -------------------------------------------------------------------------------- 1 |
    2 |

    Violations

    3 |
    4 | 5 |
    6 |
      7 | 8 | {{! Empty button }} 9 | 10 | 11 | 12 | 13 | {{! Labelless text input }} 14 | 15 | 16 | 17 | 18 | {{! Poorly Contrasting Text color }} 19 | 20 | 21 | Swoooosh 22 | 23 | 24 | 25 | {{! Usage of the element }} 26 | 27 | 28 | {{!-- template-lint-disable no-obsolete-elements --}} 29 | Friends don't let friends use <blink> tags, like this one! 30 | 31 | 32 | 33 | {{! tags without `alt` text }} 34 | 35 | {{!-- template-lint-disable require-valid-alt-text --}} 36 | 37 | 38 | 39 |
    40 |
    41 | 42 | 43 | 47 | -------------------------------------------------------------------------------- /tests/dummy/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "4.0.1", 7 | "blueprints": [ 8 | { 9 | "name": "addon", 10 | "outputRepo": "https://github.com/ember-cli/ember-addon-output", 11 | "codemodsSource": "ember-addon-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": [ 14 | "--pnpm", 15 | "--no-welcome" 16 | ] 17 | } 18 | ] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | let ENV = { 5 | modulePrefix: 'dummy', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false, 17 | }, 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | }, 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | ENV.APP.autoboot = false; 44 | } 45 | 46 | /** 47 | * This logic allows us to generate gh-pages builds with axe still enabled 48 | * by running `ember github-pages:commit --message "YOUR MESSAGE" --environment=gh-pages` 49 | */ 50 | if (environment === 'production' || environment === 'gh-pages') { 51 | ENV.locationType = 'hash'; 52 | ENV.rootURL = '/ember-a11y-testing/'; 53 | } 54 | 55 | return ENV; 56 | }; 57 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true 6 | } 7 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions', 7 | ]; 8 | 9 | module.exports = { 10 | browsers, 11 | }; 12 | -------------------------------------------------------------------------------- /tests/dummy/public/assets/img/empire-state-building-moonlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/tests/dummy/public/assets/img/empire-state-building-moonlight.png -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} {{content-for "test-head"}} 11 | 12 | 13 | 14 | 15 | 16 | {{content-for "head-footer"}} {{content-for "test-head-footer"}} 17 | 18 | 19 | {{content-for "body"}} {{content-for "test-body"}} 20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {{content-for "body-footer"}} {{content-for "test-body-footer"}} 34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/tests/integration/.gitkeep -------------------------------------------------------------------------------- /tests/integration/components/setup-global-a11y-hooks-for-render-test.gts: -------------------------------------------------------------------------------- 1 | import { click, render } from '@ember/test-helpers'; 2 | import { DEFAULT_A11Y_TEST_HELPER_NAMES, setEnableA11yAudit, setupGlobalA11yHooks, teardownGlobalA11yHooks } from 'ember-a11y-testing/test-support'; 3 | import { module, test } from 'qunit'; 4 | import { setupRenderingTest } from 'ember-qunit'; 5 | 6 | module( 7 | 'Integration | Component | setup-global-a11y-hooks-test', 8 | function (hooks) { 9 | setupRenderingTest(hooks); 10 | 11 | let actualAuditInvocationsCount = 0; 12 | 13 | function invokeAll(): boolean { 14 | return true; 15 | } 16 | 17 | async function a11yAuditFake() { 18 | actualAuditInvocationsCount += 1; 19 | } 20 | 21 | hooks.before(function () { 22 | setupGlobalA11yHooks(invokeAll, a11yAuditFake, { 23 | helpers: [...DEFAULT_A11Y_TEST_HELPER_NAMES, 'render'], 24 | }); 25 | setEnableA11yAudit(true); 26 | }); 27 | 28 | hooks.after(function () { 29 | teardownGlobalA11yHooks(); 30 | setEnableA11yAudit(); 31 | }); 32 | 33 | hooks.beforeEach(function () { 34 | actualAuditInvocationsCount = 0; 35 | }); 36 | 37 | test('it audits on render', async function (assert) { 38 | await render(); 41 | await click('button'); 42 | 43 | assert.strictEqual( 44 | actualAuditInvocationsCount, 45 | 2, 46 | 'a11yAudit was automatically called twice', 47 | ); 48 | }); 49 | }, 50 | ); 51 | -------------------------------------------------------------------------------- /tests/integration/helpers/a11y-audit-test.gts: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import type { TestContext } from '@ember/test-helpers'; 5 | import { a11yAudit, setEnableA11yAudit } from 'ember-a11y-testing/test-support'; 6 | import AxeComponent from "dummy/components/axe-component"; 7 | 8 | interface Context extends TestContext { 9 | element: Element; 10 | } 11 | 12 | module('Integration | Helper | a11yAudit', function (hooks) { 13 | setupRenderingTest(hooks); 14 | 15 | hooks.beforeEach(function () { 16 | setEnableA11yAudit(true); 17 | }); 18 | 19 | hooks.afterEach(function () { 20 | setEnableA11yAudit(false); 21 | }); 22 | 23 | test('a11yAudit runs successfully with element context', async function (this: Context, assert) { 24 | await render(); 25 | await a11yAudit(this.element); 26 | assert.ok(true, "a11yAudit ran and didn't find any issues"); 27 | }); 28 | 29 | test('a11yAudit catches violations successfully', async function (this: Context, assert) { 30 | await render( 31 | , 32 | ); 33 | 34 | await assert.rejects( 35 | >a11yAudit(this.element), 36 | /The page should have no accessibility violations. Violations:/, 37 | 'error message is correct', 38 | ); 39 | }); 40 | 41 | test('a11yAudit can use custom axe options', async function (this: Context, assert) { 42 | await render( 43 | , 44 | ); 45 | 46 | await a11yAudit(this.element, { 47 | rules: { 48 | 'button-name': { 49 | enabled: false, 50 | }, 51 | }, 52 | }); 53 | 54 | assert.ok(true, 'a11yAudit ran and used the custom options'); 55 | }); 56 | 57 | test('a11yAudit can use custom axe options as single argument', async function (assert) { 58 | await render( 59 | , 60 | ); 61 | 62 | await a11yAudit({ 63 | rules: { 64 | 'button-name': { 65 | enabled: false, 66 | }, 67 | }, 68 | }); 69 | 70 | assert.ok(true, 'a11yAudit ran and used the custom options'); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from 'dummy/app'; 2 | import config from 'dummy/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { start } from 'ember-qunit'; 7 | import { setupConsoleLogger } from 'ember-a11y-testing/test-support'; 8 | 9 | setApplication(Application.create(config.APP)); 10 | 11 | setup(QUnit.assert); 12 | setupConsoleLogger(); 13 | 14 | start(); 15 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/tests/unit/.gitkeep -------------------------------------------------------------------------------- /tests/unit/audit-test.ts: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { 3 | _normalizeRunParams, 4 | _isContext, 5 | } from 'ember-a11y-testing/test-support/audit'; 6 | import { setRunOptions } from 'ember-a11y-testing/test-support'; 7 | 8 | module('audit', function () { 9 | module('with no config', function () { 10 | test('_normalizeRunParams returns defaults when no params provided', function (assert) { 11 | let [context, options] = _normalizeRunParams(); 12 | 13 | assert.strictEqual(context, '#ember-testing-container'); 14 | assert.deepEqual(options, {}); 15 | }); 16 | 17 | test('_normalizeRunParams returns default options when only string context provided', function (assert) { 18 | let ctx = '#my-container'; 19 | let [context, options] = _normalizeRunParams(ctx); 20 | 21 | assert.strictEqual(context, '#my-container'); 22 | assert.deepEqual(options, {}); 23 | }); 24 | 25 | test('_normalizeRunParams returns default options when only Node context provided', function (assert) { 26 | let ctx = document; 27 | let [context, options] = _normalizeRunParams(ctx); 28 | 29 | assert.strictEqual(context, document); 30 | assert.deepEqual(options, {}); 31 | }); 32 | 33 | test('_normalizeRunParams returns default options when only ElementContext provided', function (assert) { 34 | let ctx = { include: ['me'] }; 35 | let [context, options] = _normalizeRunParams(ctx); 36 | 37 | assert.strictEqual(context, ctx); 38 | assert.deepEqual(options, {}); 39 | }); 40 | 41 | test('_normalizeRunParams returns defaults context when only options provided', function (assert) { 42 | let opts = {}; 43 | let [context, options] = _normalizeRunParams(opts); 44 | 45 | assert.strictEqual(context, '#ember-testing-container'); 46 | assert.deepEqual(options, opts); 47 | }); 48 | 49 | test('_normalizeRunParams returns context and options when both provided', function (assert) { 50 | let ctx = '#my-container'; 51 | let opts = {}; 52 | let [context, options] = _normalizeRunParams(ctx, opts); 53 | 54 | assert.strictEqual(context, ctx); 55 | assert.deepEqual(options, opts); 56 | }); 57 | }); 58 | 59 | module('with config', function () { 60 | test('_normalizeRunParams returns defaults when no params provided', function (assert) { 61 | setRunOptions({ 62 | rules: { 63 | // Disabled to test whether the config is 64 | // properly loaded in test environment 65 | 'image-alt': { enabled: false }, 66 | }, 67 | }); 68 | 69 | let [context, options] = _normalizeRunParams(); 70 | 71 | assert.strictEqual(context, '#ember-testing-container'); 72 | assert.ok(Object.keys(options).length > 0); 73 | }); 74 | 75 | test('_normalizeRunParams returns config options when only string context provided', function (assert) { 76 | setRunOptions({ 77 | rules: { 78 | // Disabled to test whether the config is 79 | // properly loaded in test environment 80 | 'image-alt': { enabled: false }, 81 | }, 82 | }); 83 | 84 | let ctx = '#my-container'; 85 | let [context, options] = _normalizeRunParams(ctx); 86 | 87 | assert.strictEqual(context, '#my-container'); 88 | assert.ok(Object.keys(options).length > 0); 89 | }); 90 | }); 91 | 92 | test('_isContext', function (assert) { 93 | assert.ok(_isContext('#foo')); 94 | assert.ok(_isContext(document)); 95 | assert.ok(_isContext({ include: [] })); 96 | assert.ok(_isContext({ exclude: [] })); 97 | assert.ok(_isContext({ include: [], exclude: [] })); 98 | 99 | assert.notOk(_isContext(undefined)); 100 | assert.notOk(_isContext({})); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /tests/unit/calculate-updated-href-test.ts: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupTest } from 'ember-qunit'; 3 | import { _calculateUpdatedHref } from 'ember-a11y-testing/test-support/should-force-audit'; 4 | 5 | module('Query parameter normalization', function (hooks) { 6 | setupTest(hooks); 7 | 8 | const baseUrl = 'https://www.example.com'; 9 | 10 | const paramPermutations = [ 11 | { 12 | label: 'Enable with no params', 13 | input: '/some/path', 14 | enabled: true, 15 | expected: '/some/path?enableA11yAudit', 16 | }, 17 | { 18 | label: 'Disable with no other params', 19 | input: '/some/path?enableA11yAudit', 20 | enabled: false, 21 | expected: '/some/path', 22 | }, 23 | { 24 | label: 'Enable with multiple params', 25 | input: '/some/path?foo=123&bar&baz', 26 | enabled: true, 27 | expected: '/some/path?foo=123&bar&baz&enableA11yAudit', 28 | }, 29 | { 30 | label: 'Disable with multiple params', 31 | input: '/some/path?foo=123&bar&baz&enableA11yAudit', 32 | enabled: false, 33 | expected: '/some/path?foo=123&bar&baz', 34 | }, 35 | { 36 | label: 'Already enabled, enabling noops', 37 | input: '/some/path?foo=123&bar&baz&enableA11yAudit', 38 | enabled: true, 39 | expected: '/some/path?foo=123&bar&baz&enableA11yAudit', 40 | }, 41 | { 42 | label: 'Already enabled, enabling normalizes', 43 | input: '/some/path?foo=123&bar&baz&enableA11yAudit=', 44 | enabled: true, 45 | expected: '/some/path?foo=123&bar&baz&enableA11yAudit', 46 | }, 47 | { 48 | label: 'Disable when not normalized', 49 | input: '/some/path?foo=123&bar&baz&enableA11yAudit=', 50 | enabled: false, 51 | expected: '/some/path?foo=123&bar&baz', 52 | }, 53 | { 54 | label: 'Does not normalize unknown params', 55 | input: '/some/path?foo=&bar=&baz&enableA11yAudit=', 56 | enabled: true, 57 | expected: '/some/path?foo=&bar=&baz&enableA11yAudit', 58 | }, 59 | { 60 | label: 'Normalize as first param', 61 | input: '/some/path?enableA11yAudit=&foo&bar=', 62 | enabled: true, 63 | expected: '/some/path?enableA11yAudit&foo&bar=', 64 | }, 65 | { 66 | label: 'Disable as first param', 67 | input: '/some/path?enableA11yAudit=&foo&bar=', 68 | enabled: false, 69 | expected: '/some/path?foo&bar=', 70 | }, 71 | { 72 | label: 'Not found, disabling is noop', 73 | input: '/some/path?foo=123&bar&baz=', 74 | enabled: false, 75 | expected: '/some/path?foo=123&bar&baz=', 76 | }, 77 | ]; 78 | 79 | paramPermutations.forEach(function ({ label, input, enabled, expected }) { 80 | test(`_calculateUpdatedHref ${label}`, function (assert) { 81 | const url = `${baseUrl}${input}`; 82 | const expectedHref = `${baseUrl}${expected}`; 83 | const href = _calculateUpdatedHref(url, baseUrl, enabled); 84 | 85 | assert.strictEqual( 86 | href, 87 | expectedHref, 88 | `_calculateUpdatedHref( ${url}, ${baseUrl}, ${enabled} ) -> ${href}`, 89 | ); 90 | }); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /tests/unit/format-violation-test.ts: -------------------------------------------------------------------------------- 1 | import { Result } from 'axe-core'; 2 | import { module, test } from 'qunit'; 3 | import formatViolation from 'ember-a11y-testing/test-support/format-violation'; 4 | 5 | module('Unit | Utils | formatViolation', function () { 6 | test('formats a well-formed violation and relevant html', function (assert) { 7 | let violation: Result = { 8 | id: 'test', 9 | impact: 'critical', 10 | help: 'it should be better', 11 | helpUrl: 'http://example.com', 12 | description: '', 13 | tags: [], 14 | nodes: [ 15 | { 16 | target: ['.some-class'], 17 | html: '', 18 | any: [], 19 | all: [], 20 | none: [], 21 | }, 22 | ], 23 | }; 24 | 25 | let message = formatViolation(violation, [violation.nodes[0].html]); 26 | let expected = `[critical]: it should be better \nViolated 1 time. Offending nodes are: \n\nhttp://example.com`; 27 | assert.strictEqual(message, expected); 28 | }); 29 | 30 | test('formats a well-formed violation', function (assert) { 31 | let violation: Result = { 32 | id: 'test', 33 | impact: 'critical', 34 | help: 'it should be better', 35 | helpUrl: 'http://example.com', 36 | description: '', 37 | tags: [], 38 | nodes: [], 39 | }; 40 | 41 | let message = formatViolation(violation, []); 42 | let expected = `[critical]: it should be better \nViolated 1 time.\nhttp://example.com`; 43 | assert.strictEqual(message, expected); 44 | }); 45 | 46 | test('validates violation parameter structure', function (assert) { 47 | let violation: Result = { 48 | id: 'test', 49 | help: 'it should be better', 50 | helpUrl: 'http://example.com', 51 | description: '', 52 | tags: [], 53 | nodes: [ 54 | { 55 | target: ['.some-class'], 56 | html: '', 57 | any: [], 58 | all: [], 59 | none: [], 60 | }, 61 | ], 62 | }; 63 | 64 | let expected = 65 | /formatViolation called with improper structure of parameter: violation. Required properties: impact, help, helpUrl./; 66 | 67 | assert.throws(function () { 68 | formatViolation(violation, [violation.nodes[0].html]); 69 | }, expected); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /tests/unit/get-current-route-name-test.ts: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupTest } from 'ember-qunit'; 3 | import { _getCurrentRouteName } from 'ember-a11y-testing/test-support/setup-middleware-reporter'; 4 | 5 | module('Unit | Utils | _getCurrentRouteName', function (hooks) { 6 | setupTest(hooks); 7 | 8 | test('gets the route name for the current test', function (assert) { 9 | function mockCurrentRouteName(): string { 10 | return 'index'; 11 | } 12 | 13 | const result = _getCurrentRouteName(mockCurrentRouteName); 14 | 15 | assert.strictEqual(result, 'index'); 16 | }); 17 | 18 | test('absorbs `currentRouteName` error when route name is null', function (assert) { 19 | function currentRouteNameMock(): string { 20 | throw new Error('currentRouteName shoudl be a string'); 21 | } 22 | 23 | const result = _getCurrentRouteName(currentRouteNameMock); 24 | 25 | assert.strictEqual(result, ''); 26 | }); 27 | 28 | test('bubbles up all other emitted errors', function (assert) { 29 | function mockCurrentRouteName(): string { 30 | throw new Error('Catastrophic error!'); 31 | } 32 | 33 | assert.throws(() => { 34 | _getCurrentRouteName(mockCurrentRouteName); 35 | }, /Catastrophic error!/); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "allowJs": true, 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "noImplicitAny": true, 8 | "noImplicitThis": true, 9 | "alwaysStrict": true, 10 | "strictNullChecks": true, 11 | "strictPropertyInitialization": true, 12 | "skipLibCheck": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noImplicitReturns": true, 17 | "noEmitOnError": false, 18 | "noEmit": true, 19 | "inlineSourceMap": true, 20 | "inlineSources": true, 21 | "baseUrl": ".", 22 | "module": "es6", 23 | "experimentalDecorators": true, 24 | "paths": { 25 | "dummy/tests/*": ["tests/*"], 26 | "dummy/*": ["tests/dummy/app/*", "app/*"], 27 | "ember-a11y-testing": ["addon"], 28 | "ember-a11y-testing/*": ["addon/*"], 29 | "ember-a11y-testing/test-support": ["addon-test-support"], 30 | "ember-a11y-testing/test-support/*": ["addon-test-support/*"], 31 | "*": ["types/*"] 32 | } 33 | }, 34 | "include": [ 35 | "app/**/*", 36 | "addon/**/*", 37 | "tests/**/*", 38 | "types/**/*", 39 | "test-support/**/*", 40 | "addon-test-support/**/*" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /types/dummy/index.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-a11y/ember-a11y-testing/36dcdc4be7763f9113d828ea02c83459057bcb78/types/dummy/index.d.ts -------------------------------------------------------------------------------- /types/ember-debug.d.ts: -------------------------------------------------------------------------------- 1 | import '@ember/debug'; 2 | 3 | export type DeprecationStages = 'available' | 'enabled'; 4 | 5 | declare module '@ember/debug' { 6 | export function deprecate( 7 | message: string, 8 | test: boolean, 9 | options: { 10 | /** 11 | * A unique id for this deprecation. The id can be used by Ember debugging 12 | * tools to change the behavior (raise, log or silence) for that specific 13 | * deprecation. The id should be namespaced by dots, e.g. 14 | * `"view.helper.select"`. 15 | */ 16 | id: string; 17 | /** 18 | * The version of Ember when this deprecation warning will be removed. 19 | */ 20 | until: string; 21 | /** 22 | * A namespace for the deprecation, usually the package name 23 | */ 24 | for: string; 25 | /** 26 | * Describes when the deprecation became available and enabled 27 | */ 28 | since: Partial>; 29 | /** 30 | * An optional url to the transition guide on the emberjs.com website. 31 | */ 32 | url?: string | undefined; 33 | }, 34 | ): void; 35 | } 36 | -------------------------------------------------------------------------------- /types/ember-get-config.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'ember-get-config'; 2 | -------------------------------------------------------------------------------- /types/global.d.ts: -------------------------------------------------------------------------------- 1 | // Types for compiled templates 2 | declare module 'ember-a11y-testing/templates/*' { 3 | import { TemplateFactory } from 'htmlbars-inline-precompile'; 4 | const tmpl: TemplateFactory; 5 | export default tmpl; 6 | } 7 | --------------------------------------------------------------------------------